学习总结(2022.05.27-2022.06.07)
HTTP协议
详细过程:
- 域名解析
- TCP建立连接
- 发送HTTP请求报文
- 返回HTTP响应报文
- 浏览器解析HTML代码,渲染页面,呈现给用户
其中请求报文的格式为:
- 请求行。请求行由请求方法(GET/POST),请求资源,请求协议(HTTP/1.1)组成
- 请求头。
- 空行。
- 请求体。
响应报文的格式为:
- 响应行。响应行由版本协议,状态码(200,301,404,500…),原因短语组成。
- 响应头。
- 响应体。
状态码中比较重要的有:
- 200 过程正常
- 301,302,307 重定向
- 404 未找到,意味着没有对应的资源文件
- 500 服务器异常,意味服务器代码出现bug
Servlet
Servlet 是一个在 Web 服务器中运行的小型 Java 程序。Servlet 接收和响应来自 Web 客户端的请求,通常通过HTTP协议来实现。
Servlet有两种写法:一种是通过继承GenericServlet并重写service方法;一种是通过继承HttpServlet并重写doGet方法和doPost方法。其中HttpServlet继承了GenericServlet,并将它的service方法分解成了doGet和doPost,以更好地处理GET请求和POST请求。
在Servlet中,可以通过注解的方式来配置本地程序与URL的映射关系,称为url-pattern,主要通过@WebServlet配置。一个Servlet可以配置多个url-pattern,但是一个url-pattern只能对应一个Servlet。
设置url-pattern时,可以使用通配符,如:/*、 *.html,且具有优先级,优先级判定如下:
- /xxx优先级高于*.xxx
- 如果都是/xxx,那么匹配程度越高,优先级越高
在访问静态文件时,其实访问的是一个url-pattern为/*的Servlet,该Servlet称为缺省Servlet,由tomcat提供,默认逻辑是将请求地址当作静态文件。它可以被重写,只需要在项目中设置一个url-pattern为/*的Servlet即可。
Servlet有三个生命周期,分别是init,service,destory,可以通过重写其生命周期方法来完成一些需要在这个生命周期中完成的操作。比如用init()读取文件,service()修改文件,destory()保存文件。此外,init()方法只有在浏览器第一次访问当前Servlet时调用,需要在注解中设置load-on-startup=1来令init()方法可以随着应用加载而触发执行。
ServletContext
Servlet具有一个ServletContext对象,它代表的是Servlet中的Context域,每个应用中只有唯一的一个Context域,无论哪个Servlet都可以拿到域中的数据,所以它一般用于Servlet间的共享。
API:
1 | |
ServletRequest
ServletRequest是客户端提供给Servlet的请求报文的封装体,Tomcat创建ServletRequest对象并将其作为参数传给service方法。HttpServletRequest继承了ServletRequest,主要用于封装HTTP格式的请求报文。
Request对象和对象间可以通信,这种方式叫做转发。
Request与Context相似,也拥有一个Request域,但是Request域只存在在一个Request对象中,且会随着Request对象的销毁而销毁,在多个Request对象中,Request域不共享,只有通过转发才能与其他对象共享。所以Request域一般用于存放不频繁使用的数据。
API:
1 | |
ServletResponse
ServletResponse是客户端提供给Servlet的响应报文的封装体,Tomcat创建ServletResponse对象并将其作为参数传给service方法。HttpServletResponse继承了ServletResponse,主要用于封装HTTP格式的响应报文。
API:
1 | |
会话技术
HTTP协议存在无状态性,即所有客户端发送的请求报文是完全相同的,服务器不可能通过请求报文区分客户端,也无法为每个独立的客户端处理数据。解决HTTP无状态性的即是会话技术。
会话技术分为客户端技术和服务端技术,即Cookie和Session。
Cookie
Cookie数据产生于服务器,产生之后会通过HTTP响应报文传输给客户端,客户端来保存这些数据,在客户端再次访问服务器时,会把这些数据通过HTTP请求报文再次携带回来,这样服务器就了解请求来自于哪个客户端。Cookie的本质就是在请求报文中加入了Cookie请求头,在响应报文中加入了set-Cookie请求头。
Cookie的使用方式如下
1 | |
Cookie可以进行一些设置,设置方式如下
1 | |
Cookie轻便,可以跨多主机间数据共享,而且因为存储在客户端,减轻了服务器的压力;但是Cookie只能存储字符串类型,大小也有限制,而且Cookie的安全性存在问题,只能存储一些不敏感的数据。
Session
因为数据的产生与存储均是在服务器完成,所以服务器可以在特定场景下给客户端单独开辟一块空间(即对象),该对象专门只为这个客户端服务,这个对象就被称为Session对象。
Session的本质是服务器生成一个Session对象,将该对象的编号通过Cookie返回给客户端,后续客户端访问服务器时,都会把Session的编号携带过来,那么服务器就可以获取到对应的Session对象,就能在该对象中进行只针对该客户端的数据的存取和处理。
Session的使用方式如下
1 | |
至此可以知道,服务器存在三个域:Context域,Session域,Request域。
Context域最大,一个服务器应用中只有一个,主要用于存储一些全局性的数据;Session域其次,一个客户端对应一个Session对象,一般存储客户端特有的数据;Request域最小,一个Servlet请求对应一个Request对象,一般存储访问频次不高的数据。
Session对象不会随着浏览器关闭而销毁,只是新的浏览器无法访问之前浏览器创建的Session对象;Session对象会随着服务器关闭而销毁,但是Session数据并不会被销毁,而是被序列化保存在硬盘中,只有在有效期到达,或者服务器主动调用invalidate()方法,才会被销毁。
Listener
Listener,即监听器,是EE三大组件之一,主要负责监听对象。常用的Listener为servletContextListener,用于监听ServletContext对象的创建和销毁,以替代Servlet中的init()方法和destory()方法。
Listener的实现方式如下
1 | |
Filter
Filter,即过滤器,是EE三大组件之一,主要负责权限验证,对请求进行放行或拦截,可以在请求到达关联的Servlet前,检查修改Request对象,也可以在响应返回到客户端前,检查修改Response对象。
Filter的实现方式如下
1 | |
Filter与Servlet生成关联的方式有
- Filter与Servlet设置相同的url-pattern
- Filter设置/*(常用)
Filter与Filter之间可以生成关联,具体方式为设置相同的url-pattern,Filter的执行顺序由类的首字母ASCII顺序(注解声明)或filter-mapping声明的先后顺序(web.xml声明)。
完整请求处理流程
- 浏览器解析域名,与服务器建立TCP连接
- 浏览器发送HTTP请求报文,被服务器中的Connector接收,Connector将其解析为Request对象,同时新建一个Response对象
- Connector对象将两个对象传给Enginee对象,Enginee对象进一步传递给Host对象。
- Host挑选一个合适的Context对象,再将两个对象传输给Context对象。
- Context对象根据路径查找是否有匹配的Filter需要处理该请求,如果有则交由Filter处理,Filter处理完毕再将请求发送给对应的Servlet;如果没有则直接发送给Servlet。
- Servlet接收到请求后执行service方法,执行完毕后,如果存在对应的Filter,则将处理后的两个对象返回给Filter,Filter处理完毕后再原路返回;如果没有则直接原路返回。
- 最后Connector获得Response对象,读取其中的数据后按照HTTP响应报文的格式要求,生成响应报文,发送给客户端。
- 客户端接收响应报文,解析、渲染,形成页面。
MVC
MVC分别是模型层(model),视图层(view)和控制层(controller),将应用的代码按照MVC模式分离,以达到解耦的目的。
模型层:主要存放数据的模型和对数据的操作。
视图层:主要用于显示页面或将数据结果返回给前端显示。
控制层:用于接收来自模型层的数据,并将处理后的数据返回给视图层,起到了连接模型层和视图层的作用。
MVC的核心思想就是在请求到来后,首先交给控制层处理,控制层调用模型层的代码处理数据,返回的结果再由控制层交给视图层。
三层架构是MVC设计模式的一种演进,可以将MVC进一步抽象成三个模块,分别是展示层(view、controller)、业务层(service)和数据层(Dao)。
为了进一步降低代码的耦合性,可以将模型层中对数据的操作部分提取出来,命名为dao(Data Access Object),该层可以设计一个接口,因为对数据操作时,参数类型和返回值都是不变的;这样我们就可以设计不同的实现类来实现接口,从而完成对数据的不同操作。
service层则是在controller和dao之间的业务层,主要任务是保证业务处理的实现方式的多样性。如果存在多个业务处理方式,那么就需要对controller层进行反复修改,违反了设计模式的开闭原则(对新增代码开放,对修改代码关闭),所以需要一个service层来保证可以存在多个业务处理方式。service层与dao层一样,只需要设计一个接口以保证参数类型和返回值不变,之后只需要设计实现类实现接口,并在controller层的相应位置调用即可。