- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
人活着,就得随时准备经受磨难。他已经看过一些书,知道不论是普通人还是了不起的人,都要在自己的一生中经历许多磨难。磨难使人坚强。
人和社会、一切斗争的总结局也许都是中庸而已。与其认真、不如随便、采菊东篱下、悠然见南山。有钱就寻一醉、无钱就寻一睡、与过无争、随遇而安。
hellip;…人哪,活着是这么的苦!一旦你从幸福的彼岸被抛到苦难的此岸,你真是处处走头无路;而现在你才知道,在天堂与地狱之间原来也只有一步之遥!
倘若流落在他乡异地,生活中的一切都将失去保障,得靠自己一个人去对付冷酷而严峻的现实了
青年,青年,无论受怎样的挫折和打击,都要咬着牙关挺住,因为你们完全有机会重建生活;只要不灰心丧气,每一次挫折就只不过是通往新境界的一块普通的绊脚石,而绝不会置人于死命。
你永远要宽恕众生,不论他有多坏,甚至他伤害过你,你一定要放下,才能得到真正的快乐。
也许人生仅有那么一两个辉煌的瞬间————甚至一生都可能在平淡无奇中度过......不过,每个人的生活同样也是一个世界,即是最平凡的人,也得要为他那个世界的存在而战斗。这个意义上来说,在这些平凡的世界里,也没有一天是平静的。因此,大多数
普通人不会像飘飘欲仙的老庄,时常把自己看作是一粒尘埃————尽管地球在浩渺的宇宙中也只不过是一粒尘埃罢了。
——————《平凡的世界》
@ 。
什么是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容器 创建的 Servelt 对象,这些Servlet 对象都会被放到一个集合当中 (HashMap) ,只有放到这个 HashMap 集合中的 Servlet 才能够被 WEB容器管理,自己 new 的 Servlet 对象不会被 WEB容器管理,因为我们自己 new 的Servlet对象并不没有存放到 WEB 容器当中.
WEB容器底层应该有一个 HashMap这样的集合,在这个集合当中存储了Servlet对象和请求路径之间的关系.
Servlet 必须重写的五方法分别为: init(),service(ServletRequest request, ServletResponse response),getServletConfig(),getServletInfo(),destroy(),还有一个无参构造器 什么时候创建的,什么时候调用的,什么时候销毁的.
package com.RainbowSea.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class AServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
执行如下代码,启动 Tomcat 服务器:并访问该AServlet ,通过在浏览器其当中输入: http://127.0.0.1:8080/blog01/A 。
package com.RainbowSea.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class AServlet implements Servlet {
public AServlet(){
System.out.println("AServlet 的无参构造器的调用");
}
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("AServlet 中的 init 的调用执行");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("AServlet 中的 service 的方法的调用执行");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("AServlet 中的 destroy 方法的执行调用");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<!-- 两个 name 值要保持一致-->
<servlet-name>AServlet</servlet-name>
<servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AServlet</servlet-name>
<!-- 注意: / 开始-->
<url-pattern>/A</url-pattern>
</servlet-mapping>
</web-app>
执行结果如下:
重点:
从上述控制台上显示的结果,我们可以知道: 当我们关闭服务器的时候 destroy()方法被调用了 。
在服务器关闭的时候.
因为服务器关闭的时候:要销毁 AServlet 对象的内存信息.
所以服务器在销毁 AServlet 对象内存之前,Tomcat 服务器会自动调用 AServlet 对象中的 destroy() 方法.
问题:destroy() 方法调用的时候,Servlet 对象销毁了还是没有销毁 ???
答: destroy() 方法执行的时候,AServlet 对象还在,没有被销毁,因为 destroy() 方法是对象方法。调用该方法的话需要通过对象才能调用的(反射除外),destroy()方法执行结束之后,AServlet 对象的内存才会被 Tomcat 释放.
注意点: 对应 Servlet 类我们不要编写构造器.
当我们Servlet 类中编写了一个有参数的构造器,那么如果我们没有手动再编写一个无参构造器的话,无参构造器就会消失.
如果一个 Servlet 无参构造器消失的会,如何:结果如下:
报错:500 错误。500 是一个 HTTP 协议的错误状态码。 500 一般情况下是因为服务器端的 java 程序出现了异常 。(服务器端的错误基本上都是 500 错误,服务器内部错误) 。
思考:Servlet 的无参数构造器是在对象第一次创建的时候执行的,并且只会执行一次。而 init() 方法也是在对象第一次拆创建的时候执行的,并且也只会执行一次。那么这个无参构造器可以代替 init()方法吗 ?
答:不能.
Servlet 规范中有要求,作为 javaWeb 程序员,编写 Servlet 类的时候,不建议手动编写构造器,因为编写构造器,很容易让无参数构造器消失。这个操作可能会导致 Servlet 对象无法实例化,所以 init() 方法是有存在的必要的.
package com.RainbowSea.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class AServlet implements Servlet {
// init 被翻译为初始化
// init 方法只会被执行一次,基本上和 Servlet构造器的调用同时执行,在Servlet 对象第一次被创建只会执行
// init 方法通常是完成初始化操作的。
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("AServlet is init method execute!");
}
// service 方法:是处理用户请求的核心方法
// 只要用户发送一次请求,service 方法必然会执行一次
// 发送100次请求,service方法执行100次
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("AServlet is service method execute");
}
// destroy ()方法也是只执行一次
// Tomcat 服务器在销毁AServlet 对象之前会调用一次destroy 方法
// destroy()方法在执行的时候,AServlet 对象的内存还没有被销毁,即将被销毁,因为destroy 是对象方法,需要通过对象调用
// destroy 方法中可以编写销毁前的准备
// 比如:服务器关闭的时候,AServelt 对象开启了一些资源,这些资源可能是流,也可能是数据库连接
// 那么关闭服务器的时候,要关闭这些流,关闭这些数据库连接,那么这些关闭资源的代码旧可以写到destroy()
@Override
public void destroy() {
System.out.println("AServlet is destroy method execute");
}
}
关于Servlet类中方法的说明:
init、service、destroy方法中使用最多的是哪个方法?
使用最多就是service()方法,service方法是一定要实现的,因为service方法是处理用户请求的核心方法.
什么时候使用init方法呢?
init方法很少用;通常在init方法当中做初始化操作,并且这个初始化操作只需要执行一次。例如:初始化数据库连接池,初始化线程池.... 。
什么时候使用destroy方法呢?
destroy方法也很少用;通常在destroy方法当中,进行资源的关闭。马上对象要被销毁了,还有什么没有关闭的,抓紧时间关闭资源。还有什么资源没保存的,抓紧时间保存一下.
Servlet对象更像一个人的一生:
- Servlet的无参数构造方法执行:标志着你出生了。
- Servlet对象的init方法的执行:标志着你正在接受教育。
- Servlet对象的service方法的执行:标志着你已经开始工作了,已经开始为人类提供服务了。
- Servlet对象的destroy方法的执行:标志着临终。有什么遗言,抓紧的。要不然,来不及了。
从上述内容我们知道了,当 Tomcat 服务器启动的时候,我们的构造器实际上还没有调用,也就是对象还没有被实例化创建出来。但是我们想在启动 Tomcat 服务器的时候就调用构造器。可以使用如下方式:
在我们想让Tomcat 服务器启动的时候调用哪个类的构造器,就在该类当中的 web.xml 加上如下:标签 <load-on-startup>(填写数值)</load-on-startup> : 的作用就是启动服务器的时候就会创建该对象:数值越小的越先被创建 。
<!--<load-on-startup> 的作用就是启动服务器的时候就会创建该对象:数值越小的越先被创建-->
<load-on-startup>2</load-on-startup>
举例:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<!-- 两个 name 值要保持一致-->
<servlet-name>AServlet</servlet-name>
<servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
<!-- 的作用就是启动服务器的时候就会创建该对象:数值越小的越先被创建-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AServlet</servlet-name>
<!-- 注意: / 开始-->
<url-pattern>/A</url-pattern>
</servlet-mapping>
<servlet>
<!-- 两个 name 保持一致-->
<servlet-name>BServlet</servlet-name>
<servlet-class>com.RainbowSea.servlet.BServlet</servlet-class>
<!-- 的作用就是启动服务器的时候就会创建该对象:数值越小的越先被创建-->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>BServlet</servlet-name>
<!-- 注意 / 开始-->
<url-pattern>/B</url-pattern>
</servlet-mapping>
</web-app>
我们编写一个Servlet 类直接实现了 这个 Servlet 接口 存在什么缺点没有:? 有的:我们编写的 Servlet 类实现了该 Servlet 接口中所有的方法。但是我们其实只需要其中的 service()方法 ,其他方法大部分情况下是不需要使用的。简单粗暴的实现了我们所有的方法,但是其中大部分的方法我们是不需要的,这样就显的我们的代码很丑陋了:
所以:我们不想要重写所以的方法,而是重写我们需要用的方法。这样该怎么做呢?
我们可以使用 23种设计模式中的一种: 适配器模式(Adapter) .
适配器模式的理解:举例 。
比如:如果我们的手机充电时直接插入到 220V 的电压上,手机会直接报废的,因为手机无法接受如此高的电压,怎么办呢? 我们可以该手机配备一个电源适配器——充电器,手机连接充电器(适配器),适配器连接 220V的电压,这样就问题解决了。同理的还有很多:电脑的充当器。充电热水壶等等.
编写一个 GenericServlet (abstract )抽象类适配器。 这个适配器是一个 abstract 抽象类。:我们该类实现了 Servlet 接口中所有的方法,但是其中我们常用的方法 service() 定义为是抽象方法,因为如果不想实现的方法,可以定义将该方法定义为抽象方法就不用重写了,而是交给 继承 的子类重写就好了。因为重写方法只能存在于抽象类,接口当中,所以我们这个 适配器就为了一个抽象类 。这样我们以后编写的所以 Servlet 类就只需要继承 GenericServlet 抽象类,并且只要重写其中的 service() 抽象方法即可。但是我们又可以使用 Servlet 接口中的方法,因为我们的父类GenericServlet 抽象类 实现了 Servlet 接口中的所有方法。如有必要,我们编写的Servlet 类也可以重写GenericServlet 抽象类中的方法,该重写的方法也是重写Servlet 接口中的方法.
具体编写如下:
abstract class GenericServlet 抽象类适配器 。
package com.RainbowSea.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* 编写一个标准通用的Servlet
* 以后所有的Servlet 类都不要直接实现Servlet 接口了。
* 以后所有的Servlet 类都继承 GenericServlet 类。
* GenericServlet 就是一个适配器
*/
public abstract class GenericServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 抽象方法:这个方法最常用,所以要求子类必须实现Service 方法。
*/
@Override
public abstract void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
以后编写的Servlet 都继承该 GenericServlet 抽象类适配器 。
package com.RainbowSea.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class AServlet extends GenericServlet {
/**
* 只需要重写我们常用的 service 父类中的抽象方法即可
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("AServlet 处理请求中....");
}
}
package com.RainbowSea.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class BServlet extends GenericServlet {
/**
* 只需要重写我们常用的 service 父类中的抽象方法即可
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("BServlet 处理请求中....");
}
}
问题:提供了 GenericServlet 类,init()方法还好执行吗?
答:还会执行,执行 GenericServlet 中被重写的 init()方法.
问题:init()方法是谁调用的 ?
Tomcat 服务器调用的.
问题:init()方法中的 ServletConfig 对象是谁创建的 ? 有是谁传过来的?
是由 Tomcat服务器 创建的,也是由 Tomcat 服务器传过来的。都是Tomcat 服务器干的.
Tomcat 服务器先创建了 ServleConfig 对象,然后调用 init()方法,将ServleConfig 对象传给了 init()方法 .
Tomcat 执行init()方法的伪代码 :
@Override
public void init(ServletConfig config) throws ServletException {
}
public class Tomcat {
public static void main(String[] args){
// .....
// Tomcat服务器伪代码
// 创建LoginServlet对象(通过反射机制,调用无参数构造方法来实例化LoginServlet对象)
Class clazz = Class.forName("com.bjpowernode.javaweb.servlet.LoginServlet");
Object obj = clazz.newInstance();
// 向下转型
Servlet servlet = (Servlet)obj;
// 创建ServletConfig对象
// Tomcat服务器负责将ServletConfig对象实例化出来。
// 多态(Tomcat服务器完全实现了Servlet规范)
ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();
// 调用Servlet的init方法
servlet.init(servletConfig);
// 调用Servlet的service方法
// ....
}
}
思考: init 方法中的ServileConfig 对象是小猫咪创建好的,这个ServletConfig对象目前在init 方法的参数上,属于局部变量。那么ServletConfig 对象肯定以后要在Service 方法中使用,怎么才能保证ServletConfig 对象在Service方法中能够使用呢?
答: 可以定义一个类的属性,再通过 init 方法的,将该ServleConfig 赋值上去 。
具体代码如下:
package com.RainbowSea.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* 编写一个标准通用的Servlet
* 以后所有的Servlet 类都不要直接实现Servlet 接口了。
* 以后所有的Servlet 类都继承 GenericServlet 类。
* GenericServlet 就是一个适配器
*/
public abstract class GenericServlet implements Servlet {
private ServletConfig config ;
// 这样我们在我们的子类当中的 service()方法当中也可以使用 Tomcat 服务器创建的
// ServletConfig 对象了
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
// 获取该 config 对象值
public ServletConfig getConfig() {
return config;
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 抽象方法:这个方法最常用,所以要求子类必须实现Service 方法。
*/
@Override
public abstract void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
举例:Servale 类在 service ()方法当中获取到该 ServletConfig 对象值,并使用 。
package com.RainbowSea.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class AServlet extends GenericServlet {
/**
* 只需要重写我们常用的 service 父类中的抽象方法即可
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置 response 在浏览器页面当中显示的格式类型
// 注意需要在显示之前,设置
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("AServlet 处理请求中...." + "<br>");
ServletConfig config = super.getConfig();
out.print("AServlet 当中 service 获取到 Tomcat 服务器创建的ServletConfig对象: " + config);
}
}
重点: 我们通过上述定义了一个 private ServletConfig config 成员属性,可以获取到Tomcat 创建的 ServletConfig 对象了,并在 service() 方法当中使用。但是存在一个问题:如果我们的子类要是重写了,我们父类适配器 GenericServlet 类当中的 init() 方法的话。因为多态性的缘故:在实际的运行过程中,调用的会是我们子类重写的的 init() 方法,这样导致的结果就,我们父类中的 private ServletConfig config 成员属性的值无法赋值上为 null 了。因为我们父类当中的 config 成员属性是通过父类当中的 init()方法赋值的,现在我们的子类重写了父类的 init()方法。不会调用我们父类的 init()进行赋值操作了,从而导致为了 null 值.
解决方式:将我们父类当中的 init()方法被 final 修饰,这样我们的子类就无法重写 init()方法了.
public abstract class GenericServlet implements Servlet {
private ServletConfig config ;
// 这样我们在我们的子类当中的 service()方法当中也可以使用 Tomcat 服务器创建的
// ServletConfig 对象了
@Override
public final void init(ServletConfig config) throws ServletException {
this.config = config;
}
}
上述的解决方式仍然存在一个问题:就是如果我们的子类一定要重写 init()方法该怎么办,现在你父类当中的 init()方法被 final 修饰了无法,被 子类重写.
解决方式: 我们使用 23中设计模式当中的 模板方法设计模式:
因为子类想要重写我们父类当中的 init()方法,那么我们就在父类当中,再创建一个 无参数的init()方法(方法的重载),这个方法让子类去重写,但是我们父类当中还有一个 带参数的 init(ServletConfig config) 方法,在该带有参数的init()方法当中调用我们这个提供该子类重写的 init()方法。具体代码如下:
package com.RainbowSea.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* 编写一个标准通用的Servlet
* 以后所有的Servlet 类都不要直接实现Servlet 接口了。
* 以后所有的Servlet 类都继承 GenericServlet 类。
* GenericServlet 就是一个适配器
*/
public abstract class GenericServlet implements Servlet {
private ServletConfig config ;
// 这样我们在我们的子类当中的 service()方法当中也可以使用 Tomcat 服务器创建的
// ServletConfig 对象了,final 修饰的方法无法重写
@Override
public final void init(ServletConfig config) throws ServletException {
// 赋值依旧存在,config 不会为 null
this.config = config;
// 调用子类重写后的 init()方法
this.init();
}
// 用于提供给子类重写
public void init() {
}
public ServletConfig getConfig() {
return config;
}
}
举例:在AServlet 类当中重写 init()方法,并且执行该重写的方法:
package com.RainbowSea.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class AServlet extends GenericServlet {
/**
* 只需要重写我们常用的 service 父类中的抽象方法即可
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置 response 在浏览器页面当中显示的格式类型
// 注意需要在显示之前,设置
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("AServlet 处理请求中...." + "<br>");
ServletConfig config = super.getConfig();
out.print("AServlet 当中 service 获取到 Tomcat 服务器创建的ServletConfig对象: " + config);
}
// 重写的父类当中的 init()无参方法
@Override
public void init() {
System.out.println("AServlet 重写的 init()方法执行了");
}
}
注意: GenericServlet 抽象类适配器是不需要我们自己编写的,Servlet 已经编写好了,我们只需要使用就可以了.
上述是为了理解 GenericServlet 源码,从而自己编写的 GenericServlet 适配器的。如下是 Servlet 为我们编写好的 GenericServlet 源码 ,和我们上面自己编写的设计结构是一样的.
package javax.servlet;
import java.io.IOException;
import java.util.Enumeration;
public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public GenericServlet() {
// NOOP
}
@Override
public void destroy() {
// NOOP by default
}
@Override
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
@Override
public Enumeration<String> getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
@Override
public ServletConfig getServletConfig() {
return config;
}
@Override
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
@Override
public String getServletInfo() {
return "";
}
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
// NOOP by default
}
public void log(String message) {
getServletContext().log(getServletName() + ": " + message);
}
public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
@Override
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
@Override
public String getServletName() {
return config.getServletName();
}
}
从上面的知识我们知道了 : Tomcat 服务器先创建了 ServletConfig 对象,然后调用init()方法,将ServletConfig对象传给了init()方法.
先来一波,自问自答:
ServletConfig 是什么?
package javax.servlet; 显然 ServletConfig 是一个接口.
谁去实现了这个接口呢 ?
org.apache.catalina.core.StandardWrapperFacade 这个类实现了这个 ServletConfig 接口 。
StandardWrapperFacade 源码如下:
package org.apache.catalina.core; import java.util.Enumeration; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; /** * Facade for the <b>StandardWrapper</b> object. * * @author Remy Maucherat */ public final class StandardWrapperFacade implements ServletConfig { // ----------------------------------------------------------- Constructors /** * Create a new facade around a StandardWrapper. * @param config the associated wrapper */ public StandardWrapperFacade(StandardWrapper config) { super(); this.config = config; } // ----------------------------------------------------- Instance Variables /** * Wrapped config. */ private final ServletConfig config; /** * Wrapped context (facade). */ private ServletContext context = null; // -------------------------------------------------- ServletConfig Methods @Override public String getServletName() { return config.getServletName(); } @Override public ServletContext getServletContext() { if (context == null) { context = config.getServletContext(); if (context instanceof ApplicationContext) { context = ((ApplicationContext) context).getFacade(); } } return context; } @Override public String getInitParameter(String name) { return config.getInitParameter(name); } @Override public Enumeration<String> getInitParameterNames() { return config.getInitParameterNames(); } }
Tomcat 服务器实现了ServletConfig 接口.
思考: 如果 Tomcat 服务器换成 了其他的服务器,比如换成是 Jetty 服务器,输出 ServletConfig 对象的时候,还是这个结果吗?
不一定是一样的结果了,包含的类名可能和Tomcat 不一样了,但是他们都实现了 ServletConfig 这个规范.
问题:ServletConfig对象是谁创建的,在什么时候创建的?
Tomcat 服务器(WEB服务器) 创建了ServletConfig 对象,在创建Servlet 对象的时候,同时创建ServletConfig 对象. 。
问题:ServletConfig 接口到底是干什么的? 有什么用?
Config 是 Configuration单词的缩写:n. 布局,构造;配置 。
ServletConfig 对象翻译为: Servlet 对象的配置信息对象。
一个Servlet对象就有一个配置信息对象:`web.xml <servlet></servlet>`
两个Servlet 对象就有两个配置信息对象。
ServletConfig对象中创建包装了 web.xml <servlet></servlet> 标签配置信息 : Tomcat 小喵咪解析web.xml 文件,将web.xml文件中 <servlet><init-param></init-param></servlet> 标签中的配置信息自动包装到ServletConfig 对象当中 .
如下: 注意:一个Servlet 对象就会有一个 <servlet></servlet> 配置信息标签 。如下是 AServlet对象的 xml 配置信息.
<--以上是 <servlet-name>ConfigTestServlet</servlet-name> <init-param></init-param>
<--是初始化参数,这个初始化参数信息被小喵咪封装到 ServletConfig 对象当中 -->
<servlet>
<servlet-name>AServlet</servlet-name>
<servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
<!-- 这里是可以配置一个Servlet对象的初始化信息的 注意:这里的配置对应的是上述<servlet-name>
为AServlet-->
<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/dbtest9</param-value>
</init-param>
<init-param>
<param-name>user</param-name>
<param-value>root</param-value>
</init-param>
</servlet>
public String getServletName(); // 获取该Servlet对象的名称
public String getInitParameter(String name); // 通过 <param-name>user</param-name> 标签当中的name 值获取到对应 <param-value>root</param-value> 标签当中的 value 值
public Enumeration<String> getInitParameterNames(); // 一次性获取到该Servlet对象<init-param>标签当中配置信息的 name 值
public ServletContext getServletContext(); // 获取到ServletContext对象
// 以上的4个方法: 在自己的编写的Servlet表当中也可以使用this,/super去调用(这个Servlet继承了GenericServet)
举例:获取到 web.xml 配置文件当中 : <servlet><init-param></init-param></servlet> 标签中的配置信息。因为该配置信息是会被动包装到ServletConfig 对象当中 。这里我们分别获取到 AServlet 对象和 BServlet 对象当中的配置信息.
对应web.xml 信息如下 。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>AServlet</servlet-name>
<servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
<!-- 这里是可以配置一个Servlet对象的初始化信息的 注意:这里的配置对应的是上述<servlet-name>
为AServlet-->
<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/dbtest9</param-value>
</init-param>
<init-param>
<param-name>user</param-name>
<param-value>root</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>AServlet</servlet-name>
<url-pattern>/A</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>BServlet</servlet-name>
<servlet-class>com.RainbowSea.servlet.BServlet</servlet-class>
<init-param>
<param-name>password</param-name>
<param-value>root123</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>BServlet</servlet-name>
<url-pattern>/B</url-pattern>
</servlet-mapping>
</web-app>
获取BServlet 对象当中的 web.xml 标签 的配置信息 。
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class BServlet extends GenericServlet {
/**
* 只需要重写我们常用的 service 父类中的抽象方法即可
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到 GenericServlet 父类适配器当中的 ServletConfig config成员属性
ServletConfig servletConfig = super.getServletConfig();
// 获取该Servlet对象的名称
String ServletName = servletConfig.getServletName();
writer.print(ServletName + "<br>"); // 输出 Servlet 的对象的名称
String name = "password"; // 这里如果我们直接知道name 为 pawword
// 通过 <param-name>user</param-name> 标签当中的name 值获取到对应 <param-value>root</param-value> 标签当中的 value 值
String value = servletConfig.getInitParameter(name);
writer.print(name + "--->" + value); // 页面输出
}
}
获取AServlet 对象当中的 web.xml 标签 的配置信息 。
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class AServlet extends GenericServlet {
/**
* 只需要重写我们常用的 service 父类中的抽象方法即可
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到 GenericServlet 父类适配器当中的 ServletConfig config成员属性
ServletConfig servletConfig = super.getServletConfig();
// 获取该Servlet对象的名称
String ServletName = servletConfig.getServletName();
writer.print(ServletName + "<br>"); // 输出 Servlet 的对象的名称
// 一次性获取到该Servlet对象<init-param>标签当中配置信息的 name 值
Enumeration<String> names = servletConfig.getInitParameterNames();
while(names.hasMoreElements()) { // 判断是否还有 元素,有返回 true,没有返回false。和集合当中的迭代器类似
String name = names.nextElement(); // 取出name 值
// 通过 <param-name>user</param-name> 标签当中的name 值获取到对应 <param-value>root</param-value> 标签当中的 value 值
String value = servletConfig.getInitParameter(name);
writer.print(name + "--->" + value); // 页面输出
writer.print("<br>"); // 页面换行
}
}
}
方式一: 通过先获取到 ServletConfig 对象,再通过 ServletConfig 对象获取到 ServletContext 对象 。
使用 ServletConfig 当中的 public ServletContext getServletContext(); // 获取到ServletContext对象 。
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class BServlet extends GenericServlet {
/**
* 只需要重写我们常用的 service 父类中的抽象方法即可
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 获取到 GenericServlet父类适配器当中的 private transient ServletConfig config; 成员属性
ServletConfig servletConfig = super.getServletConfig();
// 获取到 servletContext 对象
ServletContext servletContext = servletConfig.getServletContext();
System.out.println("servletContext的值: " + servletContext);
}
}
方式二: 直接通过 父类 GenericServlet 适配器当中的。this,super.getServletContext() 方法 也可以获取到ServletContext对象 。
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class BServlet extends GenericServlet {
/**
* 只需要重写我们常用的 service 父类中的抽象方法即可
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
System.out.println("servletContext的值: " + servletContext);
}
}
重点:
一个ServletContext 表示的就是一个 webapp 当中的 web.xml 文件当中下配置信息。而一个 webapp 一般只有一个 web.xml 文件。所以只要在同一个 webapp 当中,只要在同一个应用当中,所以的Servlet 对象都是共享同一个 ServletContext对象的 。举例:Tomcat 服务器中有一个 webapps ,这个 webapps 下存放了多个 webapp 。假设有 100 个 webapp ,那么就会有 100 个 ServeltContext 对象,因为一个 webapp 对应一个 web.xml 文件。总之,一个应用,一个 webapp 只有一个 web.xml文件,则只有一个 ServletContext 对象,所有该应用下/webapp下的 Servlet 对象共享.
如下:我们一个 webapp 下有两个 Servlet 对象,分别为 BServlet 和 AServlet 对象,但是他们两个的同时在同一个 webapp下的,只有一个 web.xml 文件。所以这个两个 Servlet 对象共享 一个 ServletContext 对象:
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class BServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
writer.print("BServlet 下的 servletContext的值: " + servletContext);
}
}
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class AServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
writer.print("AServlet 下的 servletContext的值: " + servletContext);
}
}
ServletContext对应显示生活中的什么例子呢?
一个教室里有多个学生,那么每一个学生就是一个Servlet,这些学生都在同一个教室当中,那么我们可以把这个教室叫做ServletContext对象。那么也就是说放在这个ServletContext对象(环境)当中的数据,在同一个教室当中,物品都是共享的。比如:教室中有一个空调,所有的学生都可以操作。可见,空调是共享的。因为空调放在教室当中。教室就是ServletContext对象.
我们想要获取到web.xml 配置文件大当中的<context-param>标签当中编写的的配置信息,需要使用到如下两个方法:
public String getInitParameter(String name); // 通过初始化参数的name获取value
public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的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对象来获取。-->
举例:
如下是 Servle 对象通过,ServletContext对象的上述两个方法,获取到 标签当中编写的配置信息:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--如下是: context-param 的编写的配置信息,该配置信息,被所有的Servlet共享
可以使用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-name>AServlet</servlet-name>
<servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AServlet</servlet-name>
<url-pattern>/A</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>BServlet</servlet-name>
<servlet-class>com.RainbowSea.servlet.BServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BServlet</servlet-name>
<url-pattern>/B</url-pattern>
</servlet-mapping>
</web-app>
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class AServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
writer.print("AServlet 下的 servletContext的值: " + servletContext + "<br>");
// 一次性获取到 <context-param> </context-param>标签当中所有的 name 值
Enumeration<String> names = servletContext.getInitParameterNames();
while(names.hasMoreElements()) { // 判断该集合是否还有元素,有返回true,没有返回false
// 获取到元素当中的 name值
String name = names.nextElement();
// 通过对象 <context-param> </context-param>标签下的name获取到对应的value值
String value = servletContext.getInitParameter(name);
writer.print(name + "--->" + value);
writer.print("<br>");
}
}
}
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class BServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
writer.print("BServlet 下的 servletContext的值: " + servletContext + "<br>");
// 一次性获取到 <context-param> </context-param>标签当中所有的 name 值
Enumeration<String> names = servletContext.getInitParameterNames();
while(names.hasMoreElements()) { // 判断该集合是否还有元素,有返回true,没有返回false
// 获取到元素当中的 name值
String name = names.nextElement();
// 通过对象 <context-param> </context-param>标签下的name获取到对应的value值
String value = servletContext.getInitParameter(name);
writer.print(name + "--->" + value);
writer.print("<br>");
}
}
}
获取应用的根路径(非常重要),因为在java源代码当中有一些地方可能会需要应用的根路径,这个方法可以动态获取应用的根路径。 在java源码当中,不要将应用的根路径写死,因为你永远都不知道这个应用在最终部署的时候,会给你的另外取起一个什么名字。所以我们需要动态获取.
public String getContextPath(); // 获取到该项目在 url 上输入的项目名
public String getRealPath(String path);// 获取文件的绝对路径(真实路径)
// 获取到这个 index.html 文件的绝对路径
// "/" 表示的是 web 目录,该方法默认是从web目录下开始找的。
// 就算你不写也是,默认从 web目录下开始找的。
String realPath = servletContext.getRealPath("/index.html");
举例:
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class AServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
// 获取到这个项目名。
String contextPath = servletContext.getContextPath();
writer.print(contextPath + "<br>");
// 获取到这个 index.html 文件的绝对路径
// "/" 表示的是 web 目录,该方法默认是从web目录下开始找的。
// 就算你不写也是,默认从 web目录下开始找的。
String realPath = servletContext.getRealPath("/index.html");
writer.print(realPath);
}
}
注意:如果想要获取到web目录下面的子目录下的,文件的绝对路径的话,需要写明该文件是在 web目录下的哪个子目录。不然无法找到的 。因为 getRealPath() 方法是从 web目录下开始寻找的.
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class AServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
// 获取到这个项目名。
String contextPath = servletContext.getContextPath();
writer.print(contextPath + "<br>");
// 获取到这个 index.html 文件的绝对路径
// "/" 表示的是 web 目录,该方法默认是从web目录下开始找的。
// 就算你不写也是,默认从 web目录下开始找的。
// 如果是web目录下的子目录的文件,需要写明对应的子目录
String realPath = servletContext.getRealPath("/test/index.html");
writer.print(realPath);
}
}
ServletContext对象还有另一个名字:应用域(后面还有其他域,例如:请求域、会话域) 如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个应用域中存储起来。存储起来后,可以被其他的 Servlet 对象获取到.
为什么是所有用户共享的数据? 不是共享的没有意义.
因为ServletContext这个对象只有一个。只有共享的数据放进去才有意义.
为什么数据量要小?
因为数据量比较大的话,太占用堆内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁。大数据量会影响服务器的性能。占用内存较小的数据量可以考虑放进去.
为什么这些共享数据很少的修改,或者说几乎不修改?
所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的.
数据量小、所有用户共享、又不修改,这样的数据放到ServletContext这个应用域当中,会大大提升效率。因为应用域相当于一个缓存,放到缓存中的数据,下次在用的时候,不需要从数据库中再次获取,大大提升执行效率.
对于ServletContext对象应用域,的存,取,删的方法如下:
// 存(怎么向ServletContext应用域中存数据)
public void setAttribute(String name, Object value); // map.put(k, v)
// 取(怎么从ServletContext应用域中取数据)
public Object getAttribute(String name); // Object v = map.get(k)
// 删(怎么删除ServletContext应用域中的数据)
public void removeAttribute(String name); // map.remove(k)
举例:如下我们定义了一个 User 类,将该 User类的实例化对象存储到 ServletContext 对象当中去,并在不同的Servlet 对象当中取出来.
package com.RainbowSea.servlet;
public class User {
private String name;
private String password;
public User() {
}
public User(String name,String password) {
this.name = name;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
AServlet 对象,存,取 。
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class AServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
// 创建一个 User 实例对象
User user = new User("Tom","123456");
// 存
servletContext.setAttribute("userObj",user); // map<K,V>
// 取 注意参数是我们 ,setAttribute设置的 K 值
//Object userObj = servletContext.getAttribute("userObj");
// 因为我们这里知道我们存储的是什么类型的数据所以可以直接强制类型转换
User userObj = (User) servletContext.getAttribute("userObj");
writer.print(userObj); // 浏览器页面输出
}
}
BServlet 对象的取 。
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class BServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 设置在浏览器页面显示的格式类型,必须在输出前设置
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 获取到GenericServlet父类适配器当中的 servletContext 对象
ServletContext servletContext = super.getServletContext();
// 取 注意参数是我们 ,setAttribute设置的 K 值
//Object userObj = servletContext.getAttribute("userObj");
// 因为我们这里知道我们存储的是什么类型的数据所以可以直接强制类型转换
User userObj = (User) servletContext.getAttribute("userObj");
writer.print(userObj); // 浏览器页面输出
}
}
两者浏览器显示的结果:注意要先访问 AServlet (先将数据存储到 ServletContext对象当去),不然BServlet 访问的话就是 null了 。
我们可以通过 ServletContext 生成日志信息:
// 通过ServletContext对象也是可以记录日志的
public void log(String message);
public void log(String message, Throwable t);
// 这些日志信息记录到哪里了?
// localhost.2021-11-05.log
// Tomcat服务器的logs目录下都有哪些日志文件?
//catalina.2021-11-05.log 服务器端的java程序运行的控制台信息。
//localhost.2021-11-05.log ServletContext对象的log方法记录的日志信息存储到这个文件中。
//localhost_access_log.2021-11-05.txt 访问日志
举例
package com.RainbowSea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class BServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
ServletContext servletContext = super.getServletContext();
servletContext.log("你好世界"); // 添加日志信息
// 达到某个条件,计入日志信息:异常
int age = 17;
if( age < 17) {
servletContext.log("对不起,您未成年,请绕行",new RuntimeException("小屁孩,快走开,不适合你"));
}
}
}
web.xml <servlet></servlet> 标签配置信息
: Tomcat 小喵咪解析web.xml 文件,将web.xml文件中 <servlet><init-param></init-param></servlet>
标签中的配置信息自动包装到ServletConfig 对象当中 。这个是 :一个 Servlet 对象就一 个 ServletConfig 对象,有 100 个 Servlet 对象就有 100 个 ServletConfig 对象。
public String getServletName(); // 获取该Servlet对象的名称
public String getInitParameter(String name); // 通过 <param-name>user</param-name> 标签当中的name 值获取到对应 <param-value>root</param-value> 标签当中的 value 值
public Enumeration<String> getInitParameterNames(); // 一次性获取到该Servlet对象<init-param>标签当中配置信息的 name 值
public ServletContext getServletContext(); // 获取到ServletContext对象
// 以上的4个方法: 在自己的编写的Servlet表当中也可以使用this,/super去调用(这个Servlet继承了GenericServet)
public String getContextPath(); // 获取到该项目在 url 上输入的项目名
public String getRealPath(String path);// 获取文件的绝对路径(真实路径)
// 获取到这个 index.html 文件的绝对路径
// "/" 表示的是 web 目录,该方法默认是从web目录下开始找的。
// 就算你不写也是,默认从 web目录下开始找的。
String realPath = servletContext.getRealPath("/index.html");
// 存(怎么向ServletContext应用域中存数据)
public void setAttribute(String name, Object value); // map.put(k, v)
// 取(怎么从ServletContext应用域中取数据)
public Object getAttribute(String name); // Object v = map.get(k)
// 删(怎么删除ServletContext应用域中的数据)
public void removeAttribute(String name); // map.remove(k)
jakarta.servlet.Servlet(接口)【爷爷】
jakarta.servlet.GenericServlet implements Servlet(抽象类)【儿子】
jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】
我们以后编写的Servlet要继承HttpServlet类。
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期!!! 。
最后此篇关于解读Servlet源码:GenericServlet,ServletConfig,ServletContext的文章就讲到这里了,如果你想了解更多关于解读Servlet源码:GenericServlet,ServletConfig,ServletContext的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
引言 深拷贝是指创建一个新对象,该对象的值与原始对象完全相同,但在内存中具有不同的地址。这意味着如果您对原始对象进行更改,则不会影响到复制的对象 常见的C#常见的深拷贝方式有以下4类:
人工智能是一种未来性的技术,目前正在致力于研究自己的一套工具。一系列的进展在过去的几年中发生了:无事故驾驶超过300000英里并在三个州合法行驶迎来了自动驾驶的一个里程碑;IBM Waston击败了
非法律建议。 欧盟《人工智能法案》 (EU AI Act) 是全球首部全面的人工智能立法,现已正式生效,它将影响我们开发和使用人工智能的方式——包括在开源社区中的实践。如果您是一位开源开发
我已经阅读了所有 HERE Maps API 文档,但找不到答案。 HERE实时流量REST API输出中的XML标签是什么意思? 有谁知道如何解释这个输出(我在我的请求中使用了接近参数)? 最佳答
我的 iPad 应用程序工作正常,我将其留在现场进行测试,但现在崩溃了[保存时?] 这是崩溃日志, Incident Identifier: 80FC6810-9604-4EBA-A982-2009A
我的程序需要 qsort 的功能才能运行,但到目前为止还没有完成它的工作。 我实际上是在对单个字符值的数组进行排序,以便将它们分组,这样我就可以遍历数组并确定每个属性的计数。我的问题是 qsort 返
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我正在尝试使用 AVR 代码对 Arduino Uno 进行编程,因为我不被允许在 9 月份开始的高级项目中使用 Arduino 库。我找到了数据表,让数字引脚正常工作,然后尝试通过 USB 串行连接
我遇到了多次崩溃,似乎 native iOS 方法正在从第三方库调用函数。这是一个例子: Thread: Unknown Name (Crashed) 0 libsystem_kernel.d
我理解如何按照 Dijkstra 算法的解释找到从头到尾的最短路径,但我不明白的是解释。在这里,从图中的图形来看,从 A 到 E 添加到我已知集合的顺序是 A,C,B,D,F,H,G,E 我没有得到的
我正在查看一些 Django 源代码并遇到了 this . encoding = property(lambda self: self.file.encoding) 究竟是做什么的? 最佳答案 其他两
Sentry 提供了很好的图表来显示消息频率,但关于它们实际显示的内容的信息很少。 这些信息是每分钟吗? 5分钟? 15分钟?小时? 最佳答案 此图表按分钟显示。这是负责存储该图数据的模型。 http
我对 JavaScript 和 Uniswap 还很陌生。我正在使用 Uniswap V3 从 DAI/USDC 池中获取价格。我的“主要”功能如下所示: async function main()
我正在尝试弄清楚我下载的 Chrome 扩展程序是如何工作的(这是骗子用来窃取 CS:GO 元素的东西,并不重要...)。我想知道使用什么电子邮件地址(或使用什么其他通信方式)来提交被钓鱼的数据。 这
引言 今天同事问了我一个问题, System.Windows.Forms.Timer 是前台线程还是后台线程,我当时想的是它是跟着UI线程一起结束的,应该是前台线程吧? 我确实没有仔
我需要一些使用 scipy.stats.t.interval() 函数的帮助 http://docs.scipy.org/doc/scipy/reference/generated/scipy.sta
当我在 Oracle 查询计划中看到类似的内容时: HASH JOIN TABLE1 TABLE2 这两个表中的哪一个是 hashed ? Oracle 文档指的是通常被散列的“较小”
我想知道 GridSearchCV 返回的分数与按如下方式计算的 R2 指标之间的差异。在其他情况下,我收到的网格搜索分数非常负(同样适用于 cross_val_score),我将不胜感激解释它是什么
本文分享自华为云社区《 多主创新,让云数据库性能更卓越 》,作者: GaussDB 数据库。 华为《Taurus MM: bringing multi-master to the clou
我真的需要一些帮助来破译这个崩溃报告: Process: Farm Hand [616] Path: /Applications/Farm
我是一名优秀的程序员,十分优秀!