JavaWeb

JavaWeb

1
2
3
4
5
public interface RequestDispatcher {
void forward(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

void include(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}
1
2
3
4
5
6
7
8
//ApplicationDispatcher ## forward()  ## doForword() ## processRequest() ## invoke() ## 
// ApplicationFilterChain ## doFilter() ## internalDoFilter()
// Filter ## doFilter()
// ApplicationDispatcher ## invoke() ##
// servlet ## service()
final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher {

}

image-20230414234950712

Servlet容器主要组件:

JavaWeb 之 Servlet 体系结构 - 格物致知_Tony - 博客园 (cnblogs.com)

Serlvet — GenericServlet - HttpServlet

ServletContext ServletConfig

Filter

Cookie Session

RequestDispatcher AsyncDispatcher

Listener

继承体系

img

JavaWeb 之 Servlet 体系结构 - 格物致知_Tony - 博客园 (cnblogs.com)

image-20230414231318801

Servlet

1
2
3
4
5
6
7
8
9
10
11
public interface Servlet {
void init(ServletConfig var1) throws ServletException;

ServletConfig getServletConfig();

void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

String getServletInfo();

void destroy();
}

HttpServlet

所以,HttpServlet 主要做了两件事:

      (1)重写了父类的 service() 方法;

      (2)重载了 service() 方法,完成分发处理;

      在我们自定义 Servlet 程序时只需要继承HttpServlet 类,然后根据业务重写 doGet() 与 doPost() 方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException(lStrings.getString("http.non_http"));
}

this.service(request, response);
}

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}

if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}

}
}

image-20230414232003262

image-20230414232102264

ServletConfig

ServletContext

Request

image-20230414231840747

Response

image-20230414231813426

Tomcat

牛逼!硬核图解 Tomcat 整体架构 - 知乎 (zhihu.com)

核心功能:

  • 处理 socket 连接,负责将网络字节流与 Request 和 Response 对象的转化;
  • 加载和管理 Servlet,以及具体处理 Request 请求;

Tomcat 支持的 io 模型有 NIO、NIO2、APR,Tomcat 支持的应用层协议有 http1.1 ajp http2.0。

Tomcat 最顶层是 server,一个 server 有多个 service,一个 service 有多个连接器和一个容器,连接器和容器之间通过 ServletRequest 和 ServletResponse 通信。

img

通过组合模式、模板方法、观察者模式和骨架抽象类,tomcat 定义了基类 LifeCycleBean 实现 LifeCycle 接口,把公共的逻辑,生命周期状态转变和维护、生命事件的触发和监听器的添加删除,子类负责实现自己的 init、stop 和 start 等方法。

  • tomcat 自定义了监听器;
  • @WebListener 注解,定义自己的监听器;

StandardServer、StandardService 等是 Server 和 Service 组件的具体实现类,它们都继承了 LifecycleBase。

StandardEngine、StandardHost、StandardContext 和 StandardWrapper 是相应容器组件的具体实现类,因为它们都是容器,所以继承了 ContainerBase 抽象基类,而 ContainerBase 实现了 Container 接口,也继承了 LifecycleBase 类,它们的生命周期管理接口和功能接口是分开的。

image-20230415092046776

在这里插入图片描述

Lifecycle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public interface Lifecycle {
String BEFORE_INIT_EVENT = "before_init";
String AFTER_INIT_EVENT = "after_init";
String START_EVENT = "start";
String BEFORE_START_EVENT = "before_start";
String AFTER_START_EVENT = "after_start";
String STOP_EVENT = "stop";
String BEFORE_STOP_EVENT = "before_stop";
String AFTER_STOP_EVENT = "after_stop";
String AFTER_DESTROY_EVENT = "after_destroy";
String BEFORE_DESTROY_EVENT = "before_destroy";
String PERIODIC_EVENT = "periodic";
String CONFIGURE_START_EVENT = "configure_start";
String CONFIGURE_STOP_EVENT = "configure_stop";

void addLifecycleListener(LifecycleListener var1);

LifecycleListener[] findLifecycleListeners();

void removeLifecycleListener(LifecycleListener var1);

void init() throws LifecycleException;
// WebappClassLoaderBase ## WebResource[] classesResources = this.resources.getResources("/WEB-INF/classes"); -- 用于加载jar包
// LifecycleBase ## this.startInternal(); -- 调用子类的startInternal() -- Service
void start() throws LifecycleException;

void stop() throws LifecycleException;

void destroy() throws LifecycleException;

LifecycleState getState();

String getStateName();

public interface SingleUse {
}
}

image-20230415111159975

LifeCycleBase

1
2
3
4
5
6
7
8
9
10
11
 public abstract class LifecycleBase implements Lifecycle {
private static final Log log = LogFactory.getLog(LifecycleBase.class);
private static final StringManager sm = StringManager.getManager(LifecycleBase.class);
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList();
private volatile LifecycleState state;
private boolean throwOnFailure;
// 子类实现 -- 抽象方法,只有类可以继承实现 --
protected abstract void initInternal() throws LifecycleException;
protected abstract void startInternal() throws LifecycleException;
protected abstract void stopInternal() throws LifecycleException;
}

image-20230415113150468

执行顺序:

Lifecycle 的 init() , start() , destory()

LifycycleBase 实现三个方法通过调用 转发调用 initInternal() , startInternal() , destoryInternal()

LifecycleBase的子类 StandardXXX 各种容器,Connector连接器,XXXBase 实现方法

StandardXXX 各种容器 :

Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public final class StandardServer extends LifecycleMBeanBase implements Server {
// 调用service的init() 方法
protected void initInternal() throws LifecycleException {
Service[] var10 = this.services;
int var11 = var10.length;
for(int var12 = 0; var12 < var11; ++var12) {
Service service = var10[var12];
service.init();
}
}

// 调用service的start() 方法
protected void startInternal() throws LifecycleException {
this.fireLifecycleEvent("configure_start", (Object)null);
this.setState(LifecycleState.STARTING);
this.globalNamingResources.start();
// 同步
synchronized(this.servicesLock) {
Service[] var2 = this.services;
int var3 = var2.length;
int var4 = 0;
while(true) {
if (var4 >= var3) {
break;
}
// 启动
Service service = var2[var4];
service.start();
++var4;
}
}
if (this.periodicEventDelay > 0) {
this.monitorFuture = this.getUtilityExecutor().scheduleWithFixedDelay(() -> {
this.startPeriodicLifecycleEvent();
}, 0L, 60L, TimeUnit.SECONDS);
}
}
// 调用service的destroy() 方法
protected void destroyInternal() throws LifecycleException {
Service[] var1 = this.services;
int var2 = var1.length;

for(int var3 = 0; var3 < var2; ++var3) {
Service service = var1[var3];
service.destroy();
}
}
}

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class StandardService extends LifecycleMBeanBase implements Service {
/**
* service的名称
*/
private String name = null;

private static final StringManager sm = StringManager.getManager("org.apache.catalina.core");
// Service所属的Server
private Server server = null;
//组件对属性改变的支持
protected final PropertyChangeSupport support = new PropertyChangeSupport(this);
//跟这个Service相关联的Connector集合
protected Connector connectors[] = new Connector[0];
/// Connector的锁
private final Object connectorsLock = new Object();
// 线程池
protected final ArrayList<Executor> executors = new ArrayList<>();
//Servlet的引擎
private Engine engine = null;
//
private ClassLoader parentClassLoader = null;
// Mapper
protected final Mapper mapper = new Mapper();
// Mapper 监听器
protected final MapperListener mapperListener = new MapperListener(this);


protected void startInternal() throws LifecycleException {
if (log.isInfoEnabled()) {
log.info(sm.getString("standardService.start.name", new Object[]{this.name}));
}

this.setState(LifecycleState.STARTING);
if (this.engine != null) {
synchronized(this.engine) {
// 启动引擎 StandardEngine.startInternal()
this.engine.start();
}
}

synchronized(this.executors) {
Iterator var2 = this.executors.iterator();
while(true) {
if (!var2.hasNext()) {
break;
}
Executor executor = (Executor)var2.next();
// 启动线程池
executor.start();
}
}
// 发布 事件
this.mapperListener.start();
synchronized(this.connectorsLock) {
Connector[] var10 = this.connectors;
int var11 = var10.length;
for(int var4 = 0; var4 < var11; ++var4) {
Connector connector = var10[var4];
if (connector.getState() != LifecycleState.FAILED) {
// 启动连接 Connector.startInternal()
connector.start();
}
}

}
}
}

**连接器 Connector

连接器进一步细化:

  • 监听网络端口;
  • 接受网络请求;
  • 读取网络字节流;
  • 根据应用层协议解析字节流,生成统一的 tomcat request 和 tomcat response 对象;
  • 将 tomcat request 对象转成 servletRequest;
  • 调用 servlet 容器,得到 servletResponse;
  • 将 servletResponse 转成 tomcat response;
  • 将 tomcat response 转成网络字节流;
  • 将响应字节流写回给浏览器;

按照高内聚的功能划分:

  • 网络通信;
  • 应用层协议解析;
  • tomcat request/response 与 servlet request/response 的转换;

组件通过接口交互,好处是封装变化。Endpoint 负责提供字节流给 Processor,Processor 负责提供 tomcat request 对象给 Adapter,Adapter负责提供 Servlet Request 给容器。

其中 Endpoint 和 Processor 抽象组装在一起形成了 ProtocolHandler 组件。

img

ProtocolHandler

Endpoint

接口,抽象实现类是 AbstractEndpoint,具体子类在 NioEndpoint 和 Nio2Endpoint,其中两个重要组件:Acceptor 和 SocketProcessor。

Acceptor 用于监听 Socket 连接请求,SocketProcessor 用于处理收到的 Socket 请求,提交到线程池 Executor 处理。

Processor

接收 Endpoint 的 socket,读取字节流解析成 tomcat request 和 response,通过 adapter 将其提交到容器处理。Processor 的具体实现类 AjpProcessor、Http11Processor 实现了特定协议的解析方法和请求处理方式。

img

Endpoint 接收到 socket 连接后,生成一个 socketProcessor 交给线程池处理,run 方法会调用 Processor 解析应用层协议,生成 tomcat request 后,调用 adapter 的 service 方法。

image-20230415155722946

image-20230415155158737

Adapter

ProtocolHandler 接口负责解析请求生成 tomcat requst,CoyoteAdapter 的 service 方法,将 Tomcat Request 对象,转成 ServletRequest,再调用 service 方法。

容器 Container

1
2
3
4
5
6
// 实现接口,设置 Container
public interface Contained {
Container getContainer();

void setContainer(Container var1);
}

image-20230415114210008

ContainerBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
// 保存当前容器的子容器 (1 -- n)
protected final HashMap<String, Container> children = new HashMap();
protected int backgroundProcessorDelay = -1;
protected ScheduledFuture<?> backgroundProcessorFuture;
protected ScheduledFuture<?> monitorFuture;
protected final List<ContainerListener> listeners = new CopyOnWriteArrayList();
protected Cluster cluster = null;
private final ReadWriteLock clusterLock = new ReentrantReadWriteLock();
protected String name = null;
// 当前容器的父容器
protected Container parent = null;
protected ClassLoader parentClassLoader = null;
// 管理子容器的执行顺序 ---Value, Basic
protected final Pipeline pipeline = new StandardPipeline(this);
private volatile Realm realm = null;
private final ReadWriteLock realmLock = new ReentrantReadWriteLock();
protected static final StringManager sm = StringManager.getManager("org.apache.catalina.core");
protected boolean startChildren = true;
protected final PropertyChangeSupport support = new PropertyChangeSupport(this);
protected volatile AccessLog accessLog = null;
private volatile boolean accessLogScanComplete = false;
private int startStopThreads = 1;
protected ExecutorService startStopExecutor;
}

image-20230415114642720

容器的层次结构

父子关系的 Engine、Host、Context、Wrapper 和 Servlet。

Context 表示 web 应用程序、

wrapper 表示 servlet、

context 有多个 wrapper,

host 也有多个 context。

img

Host 代表的是一个虚拟主机,或者说一个站点,可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可以部署多个 Web 应用程序;Engine 表示引擎,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine。

img

容器通过 Pipeline-Valve 责任链,对请求一次处理,invoke 处理方法,每个容器都有一个 Pipeline,触发第一个 Valve,这个容器的 valve 都会被调到,不同容器之间通过 Pipeline 的 getBasic 方法,负责调用下层容器的第一个 Valve。

img

整个调用连由连接器中的 adapter 触发,调用 engine 中的第一个 Valve。

1
2
// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

wrapper 容器的最后一个 valve 创建一个 filter 链,并调用 doFilter 方法,最终会调用到 servlet 的 service 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 final class StandardWrapperValve
extends ValveBase {

@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// ...

ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {

// dofilter
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
// dofilter
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}

}
} catch() {
// ...
}
}
}

ServletContext 是 tomcat 中的一个成员变量,spring 中的 ApplicationContext 是 servlet 规范中的 ServletContext 属性。

Value

容器的链保存,通过invoke()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface Valve {
Valve getNext();

void setNext(Valve var1);

void backgroundProcess();

void invoke(Request var1, Response var2) throws IOException, ServletException;

boolean isAsyncSupported();
}

public abstract class ValveBase extends LifecycleMBeanBase implements Contained, Valve {
protected static final StringManager sm = StringManager.getManager(ValveBase.class);
protected boolean asyncSupported;
// 当前容器
protected Container container;
protected Log containerLog;
// 下一个容器
protected Valve next;
}

源码

Container

接口,用于管理容器

1
2
3
4
5
6
public interface Container extends Lifecycle {
String ADD_CHILD_EVENT = "addChild";
String ADD_VALVE_EVENT = "addValve";
String REMOVE_CHILD_EVENT = "removeChild";
String REMOVE_VALVE_EVENT = "removeValve";
}

image-20230415092331279

ContainerBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
private static final Log log = LogFactory.getLog(ContainerBase.class);
// 当前容器的 子容器集合 (1 -- n)
protected final HashMap<String, Container> children = new HashMap();
protected int backgroundProcessorDelay = -1;
protected ScheduledFuture<?> backgroundProcessorFuture;
protected ScheduledFuture<?> monitorFuture;
protected final List<ContainerListener> listeners = new CopyOnWriteArrayList();
protected Log logger = null;
protected String logName = null;
protected Cluster cluster = null;
private final ReadWriteLock clusterLock = new ReentrantReadWriteLock();
protected String name = null;
protected Container parent = null;
protected ClassLoader parentClassLoader = null;
protected final Pipeline pipeline = new StandardPipeline(this);
private volatile Realm realm = null;
private final ReadWriteLock realmLock = new ReentrantReadWriteLock();
protected static final StringManager sm = StringManager.getManager("org.apache.catalina.core");
protected boolean startChildren = true;
protected final PropertyChangeSupport support = new PropertyChangeSupport(this);
protected volatile AccessLog accessLog = null;
private volatile boolean accessLogScanComplete = false;
private int startStopThreads = 1;
// 线程池
protected ExecutorService startStopExecutor;
}

image-20230415092342531

StandardContext

image-20230415074618633

Servlet规范

一、Servlet规范介绍:

1.JavaEE规范中的一种。
2.作用:
1)在servlet规范中,指定【动态资源文件】开发步骤
2)在servlet规范中,指定Http服务器调用动态资源文件的规则
3)在servlet规范中,指定Http服务器管理动态资源文件实例对象规则

二、Servlet接口实现类:

1.Servlet接口是Servlet规范下的一个接口,这个接口存在Http服务器jar包
2.Tomcat服务器下lib文件中有Servlet规范的jar包 (javax.servlet.Servlet接口)
3.Servlet规范中,http服务器能调用的【动态资源文件】必须为一个servlet接口实现类。

三、Servlet接口实现类开发步骤:

第一步:创建一个java类继承于HttpServlet父类,使其成为Servlet接口的实现类
第二步:重写HttpServlet中两个方法,doGet和doPost

浏览器———–>oneServle.doGet();

第三步:将Servlet接口实现类信息【注册】到Tomcat服务器。

【网站】———->【web】———>【WEB-INF】———>web.xml

<!---将Servlet接口实现类类路径的地址交给Tomcat--->
 <servlet>
      <servlet-name>mm</servlet-name><!----声明一个变量存储servlet接口实现类类路径--->
      <servlet-class>com.servlettext.controller.OneServlet</servlet-class><!-----声明servlet接口实现类类路径-->
mmm /one http://localhost:8080/myWeb/one

四、Servlet对象的生命周期:

1.网站中所有Servlet接口实现类实例对象,只能由HTTP服务器负责创建,开发人员不能手动去创建Servlet接口实现类的实例对象
2.在默认没情况下,Http服务器接受到对于当前Servlet接口实现类第一次请求时,自动创建这个Servlet接口实现类的实例对象。在手动配置的情况下,要求Http服务器在启动时自动创建某个Servlet接口实现类的实例对象。

mm com.servlettext.controller.OneServlet 30 1 2 3 4 5 3.在Http服务器运行期间一个Servlet接口实现类只能被创建出一个实例对象。 4.在Http服务器关闭时,自动将网站中所有Servlet对象进行销毁。

五、HttpServletResponse接口:

1.介绍:

1)HttpServletResponse接口来自于Servlet规范中,在Tomcat中存在servlet-api.jar

2)HttpServletResponse接口实现类由Http服务器负责提供

3)HttpServletResponse接口负责将doGet/doPost方法执行结果写入到【响应体】交给浏览器

4)开发人员习惯于将HttpServletResponse接口修饰的对象称为【响应对象】

2.主要功能:

1)将执行结果以二进制形式写入响应体
2)设置响应头中【content-type】属性值,从而控制浏览器使用对应编译器将响应体二进制数据编译为【文字、图片、视频】
3)设置响应头中【location】属性,将一个请求地址赋值给location,从而控制浏览器向指定服务器发送请求

六、HttpServletRequest接口:

1.介绍:

1)HttpServletRequest接口来自于Servlet规范中,在Tomcat中存在servlet-api.jar
2)HttpServletRequest接口实现类由Http服务器负责提供
3)HttpServletRequest接口负责在doGet/doPost方法运行时读取Http请求协议包中信息
4)开发人员习惯于将HttpServletRequest接口修饰的对象称为【请求对象】

2.作用:

1)可以读取Http请求协议包中【请求行】信息 2)读取保存在Http请求协议包中【请求头】或者【请求体】中的请求参数信息
3)代替浏览器向Http服务器申请资源文件调用

七、请求对象和响应对象生命周期

在Http服务器接收到浏览器发送的【Http请求协议包】之后,自动的为当前的【Http请求协议包】生成一个【请求对象】和一个【响应对象】
在Http服务器调用doGet/doPost方法,负责将
【请求对象】和【响应对象】作为实参传递到方法,确保doGet/doPost正确执行
在Http服务器准备推送Http响应协议包之前负责将本次请求关联的【请求对象】和【响应对象】销毁
【请求对象】和【响应对象】生命周期贯穿一次请求的处理过程中

八、欢迎资源文件

1.默认欢迎资源文件:
用户发送了一个针对某个网站的【默认请求】时,此时由Http服务器自动从当前网站返回的资源文件。
正常请求:http://localhost:8080/myWeb/index.html
默认请求:http://localhost:8080/myWeb/

2.Tomcat对于默认欢迎资源文件定位规则
1)规则位置:Tomcat安装位置/conf/web.xml
2)规则命令:

index.html index.htm index.jsp 1 2 3 4 5 3.设置当前网站的默认欢迎资源文件规则 1)规则位置:网站/web/web-inf/web.xml 2)规则命令: login.html 1 2 3 3)网站设置自定义默认文件定位规则,此时Tomcatt自带的定位规则将消失。

九、Http状态码

1.介绍:

1)由三位数字组成的符号
2)Http服务器在推送响应包之前,根据本次请求处理情况将Http状态码写入到响应包中【状态行】上
3)作用:如果Http服务器针对本次请求,返回了对应的资源文件。通过Http状态码通知浏览器应该如何处理这个结果
如果iHttp服务器针对本次请求,无法返回对应的资源文件通过Http状态码向浏览器解释不能提供服务的原因

2.分类:

1)组成 100—-599 分为五大类
2)1xx:
(最有特征)100:通知浏览器本次返回的资源文件并不是一个独立的资源文件,需要浏览器在接受响应包之后,继续向Http服务器索要依赖的其他资源文件。
3)2xx:
(最有特征)200:通知浏览器本次返回的是一个完整的独立资源文件,浏览器接受之后不需要去索要其他关联文件
4)3xx:
(最有特征)302:通知浏览器本次返回的不是一个资源文件的内容,而是资源文件的地址需要浏览器根据这个地址自动发起请求来索要这个资源文件。
response.sendRedirect(“资源文件地址”)写入到响应头中location,这个行为导致Tomcat将302状态码写入到状态行
5)4XX:
404:通知浏览器,由于在服务端没有定位到被访问的资源文件因此无法提供服务
405:通知浏览器,在服务端已经定位到被访问的资源文件(servlet)。但是这个Servlet对于浏览器采取的请求方式不能处理
6)5xx:
500:通知浏览器,在服务端已经定位到被访问的资源文件(Servlet).这个servlet可以接收浏览器采用请求方式,但是servlet在处理请求期间,由于java异常导致处理失败

十、多个servlet之间调用规则:

1.前提条件:

某些来自于浏览器发送请求,往往需要服务端中多个servlet进行协同处理,但是浏览器一次只能访问一个servlet,导致客户需要手动通过浏览器发起多次请求才能得到服务。这样增加客户获得服务难度,客户选择放弃访问此网站。

2.提高用户使用感受规则:

无论本次请求涉及到多少个Servlet,用户只需要【手动】通知浏览器发起一次请求即可。

3.实现方案:

1)重定向解决方案 2)请求转发解决方案

  1. 重定向解决方案:
    工作原理:

用户第一次通过手动的方式通知浏览器访问OneServlet,oneServlet工作完毕后,将TwoServlet地址写入到响应头的location属性中,导致Tomcat将302的状态码写入到状态行中,在浏览器接收到响应包之后,读取到302状态码,此时浏览器自动根据响应头中location属性地址发起第二次请求,访问TwoServlet去完成请求中剩余的任务

实现命令:
response.sendRedirect(请求地址);降低至写入到响应包的响应头中location属性
特征:

1)请求地址:
即可以把当前网站(/网站名/资源文件名)内部的资源文件地址发送给浏览器,也可以把其他网站(http://ip地址:端口号/网站名/资源文件名)资源文件地址发送给浏览器。
2)请求次数: 浏览器至少发送两次请求,只有第一次是用户手动发送的,后续请求是浏览器自动发送的。
3)请求方式:
重定向解决方案是通过地址栏发起下一次请求,因此通过重定向解决方案调用的资源文件接受的请求方式一定是【Get】

缺点:
重定向解决方案需要在浏览器与服务器之间进行多次往返,大量时间消耗在往返次数上,增加用户等待服务时间

2.请求转发解决方案:
原理:

用户第一次通过手动的方式要求浏览器访问OneServlet,oneServlet工作完毕后,通过当前的请求对象代替浏览器向Tomcat发送请求,申请调用TwoServlet。Tomcat在接收到这个请求之后,自动调用TwoServlet完成剩余任务

实现命令:

请求对象代替浏览器向Tomcat发送请求
//1.通过当前请求对象生成资源文件申请报告对象
RequestDispatcher report=request.getRequestDispatcher(:” /资源文件名称”);
//2.将报告对象发送给Tomcat
report.forward(当前请求对象,当前响应对象);
1
2
3
4
5
优点:
1)无论本次请求涉及到多少个Servlet,用户只需要手动向浏览器发送一次请求
2)Servlet之间调用发生在服务端计算器,节省浏览器与服务端之间的往返次数。增加处理服务的速度
特征:

1)请求次数: 在请求转发过程中,浏览器只发送一次请求
2)请求地址 只能向Tomcat服务器申请调用当前网站下的资源文件地址
request.getRequestDispathcer(“/资源文件名”); 不能写网站名**
3)请求方式请求转发过程中,浏览器只发送了一个Http请求协议包,参与本次请求的所有Servlet共享同一个请求协议包,因此这些Servlet接受的请求方式与浏览器的请求方式保持一致。

十一、多个Servlet之间数据共享实现方案:

数据共享:
OneServlet工作完毕后,将产生的数据交给TwoServlet使用

Servlet规范中提供四种数据共享方案
1)ServletContext接口 2)Cookie类 3)HttpSession接口 4)HttpServletRequest接口

1.ServletContext接口

介绍:

1)来自于Servlet规范中的一个接口。在Tomcat中存在servlet-api.jar在Tomcat中负责提供接口的实现类
2)如果两个Servlet来自于同一个网站。彼此之间通过网站的ServletContext实例对象实现数据共享
3)ServletContext实例对象称为【全局作用域对象】

工作原理:

每个网站都存在一个全局作用域对象。【相当于】一个Map集合。在这个网站中OneServlet可以将一个数据存入到全局作用域对象中,当前网站中其他Servlet此时都可以从全局作用域对象中得到这个数据进行使用

全局作用域对象生命周期:
1)在Http服务器启动过程中,自动为当前网站内存中创建一个全局作用域对象
2)在Http服务器运行期间,一个网站只有一个全局作用域对象
3)在Http服务器运行期间,全局作用域对象一直存活状态
4)在Http服务器准备关闭时,将当前网站中全局作用域对象进行销毁

全局作用域对象生命周期贯穿于网站整个运行期间

命令的实现:同一个网站中,OneServlet将数据共享给TwoServlet

1
2
3
4
5
6
7
8
9
10
11
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){

//1.通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
ServletContext application=request.getServletContext();
//2.将数据添加到全局作用域对象作为【共享数据】
application.setAttribute(" key1",数据);


}
}
1
2
3
4
5
6
7
8
9
10
11

TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){

//1.通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
ServletContext application=request.getServletContext();
//2.从全局作用域对象得到指定关键字对应数据
Object 数据= application.getAttribute(" key1");

​ }
}

介绍:
1)Coookie来自于Servlet规范中的一个工具类,存在于Tomcat提供的servlet-api.jar包
2)如果两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,此时借助于Cookie对象进行数据共享
3)Cookie存放的当前用户的私人数据,在共享数据的过程中提高服务质量

原理:

用户通过浏览器第一次向myweb网站发送请求申请OneServlet,OneServlet在运行期间创建一个Cookie存储当前用户相关数据,OneServlet工作完毕后,【将Cookie写入到响应头中】交还给当前浏览器。
浏览器收到响应包之后,将Cookie存储在浏览器的缓存中,一段时间后,用户通过同一个浏览器再次向myweb发送请求申请TwoServlet时,浏览器需要无条件将myweb网站之前推送过来的Cookie,写入到请求头中,发送过去。此时TwoServlet在运行时通过读取请求头中的Cookie中信息,得到OneServlet提供的共享数据。

实现命令:同一个网站中的OneServlet与TwoServlet借助于Cookie实现数据共享
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.创建一个Cookie对象保存共享数据(当前用户数据)
Cookie card=new Cookie(“key1”,”abc“);
Cookie card=new Cookie(“key2”,”efg“);
//cookie相当于Map
//一个Cookie对象存放一个键值对
//键值对中的value只能是String
//键值对中的key不能是中文
//2。将cookie写入到响应头,交给浏览器
response.addCookie(card);
response.addCookie(card2);
}
}

TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.调用请求对象从请求头得到浏览器返回的cookie
Cookie cookieArray[ ]=request.getCookies();
//2。循环遍历数据得到每一个Cookie的key与value
for(Cookie card; cookieArray){
String key=card.getName();//读取key “key1”
Strng value=card.getValue();//读取value “ abc”
提供较好的服务
}
}
}

销毁时机:
1)在默认情况下,Cookie对象存放在浏览器的缓存中吗,只要浏览器关闭Cookie对象就会销毁
2)手动设置情况下,可以要求浏览器将接收到的Cookie存放在客户端计算机的硬盘上,同时需要制定硬盘上的存活时间.存活时间范围内,关闭浏览器,关闭客户端计算机,关闭服务器,都不会导致Cookie被销毁
。在存活时间到达,Cookie自动的在硬盘上被删除

cookie.setMaxAge(60);//cookie存活60秒
1

3.HttpSession接口:

介绍:
1)HttpSession接口来自于Servlet规范下的一个接口,存在于Servlet-api.jar,其实现类由Http服务器提供。
2)如果两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,此时借助于HttpSession对象进行数据共享
3)开发人员习惯于将HttpSession接口修饰对象称为【会话作用域对象】

HttpSession 与 Cookie 区别:【面试题】
1)存储位置: 一个在天上,一个在地下
Cookie:存放在客户端计算机(浏览器内存/硬盘) HttpSession:存放在服务端计算机内存

2)数据类型: Cookie对象存储共享数据类型只能是String
HttpSession对象可以存储任意类型的共享数据Object

3)数据数量: 一个Cookie对象只能存储一个共享数据
HttpSession使用map集合存储共享数据,所以可以 存储任意数量共享数据

4)参照物: Cookie相当于客户在服务端【会员卡】 HttpSession相当于客户在服务端【私人保险柜】

命令实现: 同一个网站(myWeb)下OneServlet将数据传递给TwoServlet
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
HttpSession session = request.getSession();
//2.将数据添加到用户私人储物柜
session.setAttribute(“key1”,共享数据)
}
}
1

​ 浏览器访问/myWeb中TwoServlet
1
TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
​ //1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
​ HttpSession session = request.getSession();
​ //2.从会话作用域对象得到OneServlet提供的共享数据
​ Object 共享数据 = session.getAttribute(“key1”);
​ }
​ }

Http服务器如何将用户与HttpSession关联起来 使用cookie

getSession() 与 getSession(false)
1)getSession(): 如果当前用户在服务端已经拥有了自己的私人储物柜. 要求tomcat将这个私人储物柜进行返回 如果当前用户在服务端尚未拥有自己的私人储物柜 要求tocmat为当前用户创建一个全新的私人储物柜

2)getSession(false):如果当前用户在服务端已经拥有了自己的私人储物柜. 要求tomcat将这个私人储物柜进行返回 如果当前用户在服务端尚未拥有自己的私人储物柜 此时Tomcat将返回null

HttpSession销毁时机:
1.用户与HttpSession关联时使用的Cookie只能存放在浏览器缓存中.
2.在浏览器关闭时,意味着用户与他的HttpSession关系被切断
3.由于Tomcat无法检测浏览器何时关闭,因此在浏览器关闭时并不会
导致Tomcat将浏览器关联的HttpSession进行销毁
4.为了解决这个问题,Tomcat为每一个HttpSession对象设置【空闲时间】
这个空闲时间默认30分钟,如果当前HttpSession对象空闲时间达到30分钟
此时Tomcat认为用户已经放弃了自己的HttpSession,此时Tomcat就会销毁
掉这个HttpSession

HttpSession空闲时间手动设置
在当前网站/web/WEB-INF/web.xml

5

1
2
3
4

4.HttpServletRequest接口实现数据共享:

介绍:
1) 在同一个网站中,如果两个Servlet之间通过【请求转发】方式进行调用,
彼此之间共享同一个请求协议包。而一个请求协议包只对应一个请求对象 因此servlet之间共享同一个请求对象,此时可以利用这个请求对象在两个
Servlet之间实现数据共享 2) 在请求对象实现Servlet之间数据共享功能时,开发人员将请求对象称为【请求作用域对象】

命令实现: OneServlet通过请求转发申请调用TwoServlet时,需要给TwoServlet提供共享数据
OneServlet{
public void doGet(HttpServletRequest req,HttpServletResponse response){
//1.将数据添加到【请求作用域对象】中attribute属性
req.setAttribute(“key1”,数据); //数据类型可以任意类型Object
//2.向Tomcat申请调用TwoServlet
req.getRequestDispatcher(“/two”).forward(req,response)
}
}
1
2
3
4
5
6
7
8
TwoServlet{
public void doGet(HttpServletRequest req,HttpServletResponse response){
//从当前请求对象得到OneServlet写入到共享数据
Object 数据 = req.getAttribute(“key1”);
}
}
1
2
3
4
5
6

十二、监听器接口

介绍:
1)一组来自于Servlet规范下接口,共有8个接口。在Tomcat存在servlet-api.jar包
2)监听器接口需要由开发人员亲自实现,Http服务器提供jar包并没有对应的实现类
3)监听器接口用于监控【作用域对象生命周期变化时刻】以及【作用域对象共享数据变化时刻】

作用域对象:
1)在Servlet规范中,认为在服务端内存中可以在某些条件下为两个Servlet之间提供数据共享方案的对象,被称为【作用域对象】

2)Servlet规范下作用域对象:
ServletContext: 全局作用域对象
HttpSession : 会话作用域对象
HttpServletRequest:请求作用域对象

监听器接口实现类开发规范:三步
1)根据监听的实际情况,选择对应监听器接口进行实现

2)重写监听器接口声明【监听事件处理方法】

3)在web.xml文件将监听器接口实现类注册到Http服务器

ServletContextListener接口:
1)作用:通过这个接口合法的检测全局作用域对象被初始化时刻以及被销毁时刻

2)监听事件处理方法:

public void contextInitlized() :在全局作用域对象被Http服务器初始化被调用

public void contextDestory(): 在全局作用域对象被Http服务器销毁时候触发调用

ServletContextAttributeListener接口
1)作用:通过这个接口合法的检测全局作用域对象共享数据变化时刻
2)监听事件处理方法:

public void contextAdd():在全局作用域对象添加共享数据
1
public void contextReplaced():在全局作用域对象更新共享数据
1
public void contextRemove():在全局作用域对象删除共享数据
1
全局作用域对象共享数据变化时刻
ServletContext application = request.getServletContext();
application.setAttribute(“key1”,100); //新增共享数据
application.setAttribute(“key1”,200); //更新共享数据
application.removeAttribute(“key1”); //删除共享数据
1
2
3
4

十三、Filter接口(过滤器接口)

介绍:
1)来自于Servlet规范下接口,在Tomcat中存在于servlet-api.jar包
2)Filter接口实现类由开发人员负责提供,Http服务器不负责提供
3)Filter接口在Http服务器调用资源文件之前,对Http服务器进行拦截

具体作用:
1)拦截Http服务器,帮助Http服务器检测当前请求合法性
2)拦截Http服务器,对当前请求进行增强操作

Filter接口实现类开发步骤:三步
1)创建一个Java类实现Filter接口
2)重写Filter接口中doFilter方法
3)web.xml将过滤器接口实现类注册到Http服务器

Filter拦截地址格式
1)命令格式:

oneFilter 拦截地址 1 2 3 4 2)命令作用: 拦截地址通知Tomcat在调用何种资源文件之前需要调用OneFilter过滤进行拦截

3)要求Tomcat在调用某一个具体文件之前,来调用OneFilter拦截

<url-pattern>/img/mm.jpg</url-pattern>

1
4)要求Tomcat在调用某一个文件夹下所有的资源文件之前,来调用OneFilter拦截

<url-pattern>/img/*</url-pattern>

1
5)要求Tomcat在调用任意文件夹下某种类型文件之前,来调用OneFilter拦截

1
6)要求Tomcat在调用网站中任意文件时,来调用OneFilter拦截



JavaWeb
http://example.com/2023/06/01/Java精通/Javaweb/
作者
where
发布于
2023年6月1日
许可协议