学习链接:https://www.liaoxuefeng.com/wiki/1252599548343744/1255945497738400
大部分内容来自此教程,以下只是我的学习笔记。
概念
JavaEE: Java Platform Enterprise Edition Java企业平台。它不是一个软件产品,是一种软件架构和设计思想。最核心的组件就是基于Servlet标准的Web服务器。
Web开发:编写服务器程序来处理客户端请求
HTTP协议
浏览器发出请求给web服务器,web服务器发送html网页给浏览器。发送的HTTP请求和响应格式例如:
- 请求
|
|
- 响应
|
|
返回的响应码:
- 1xx 服务器收到请求,需要请求者继续执行操作
- 2xx 成功
- 3xx 重定向
- 4xx 客户端错误
- 5xx 服务端错误
请求和响应报文处理细节:
HTTP请求和响应都由HTTP Header和HTTP Body构成,其中HTTP Header每行都以
\r\n结束。如果遇到两个连续的\r\n,那么后面就是HTTP Body。浏览器读取HTTP Body,并根据Header信息中指示的Content-Type、Content-Encoding等解压后显示网页、图像或其他内容。
Servlet基础介绍
如果服务端要自己处理HTTP的请求,要包含很多动作,如识别请求头是否正确、复用TCP链接、异常处理等,非常繁琐。这些底层操作其他可以由JavaEE来操办,这样我们可以专注于实现我们真正的服务。
为了实现这一目的,JavaEE提供了Servlet API,我们使用Servlet API编写自己的Servlet来处理HTTP请求,Web服务器实现Servlet API接口,实现底层功能
这里的Servlet API是一个jar包,可以通过maven引入。
一个简单的代码例子
|
|
一个Servlet总是继承自
HttpServlet,然后覆写doGet()或doPost()方法。我们使用Servlet API时,并不直接与底层TCP交互,也不需要解析HTTP协议,因为HttpServletRequest和HttpServletResponse就已经封装好了请求和响应。
如何运行我们的web应用
将我们servlet的web应用打包为war,需要将其放在支持Servlet API的Web服务器(例如开源免费的Tomcat)。这样就可以处理浏览器发送的请求。
传统的方式是:下载Tomcat,把生成的war放到webapps目录下,再启动。但是这种方法比较麻烦,不好调试。廖雪峰老师也介绍了更方便的方法:
- maven引入Tomcat的jar包(Servlet API也包含在Tomcat的包里)
- 编写自己的Servlet方法
- 编写一个main方法,用于启动Tomcat服务器
- 执行main方法即可
Servlet实现细节
注解
Servlet使用注解表示处理的路径。例如:
|
|
表示该Servlet能处理/hello路径的请求。一般是由Web Server统一接收请求,再dispatch(路径转发)到相应的Servlet去处理。
HttpServletRequest
封装的请求报文头。常用方法:
- getMethod():返回请求方法,例如,
"GET","POST"; - getRequestURI():返回请求路径,但不包括请求参数,例如,
"/hello"; - getQueryString():返回请求参数,例如,
"name=Bob&a=1&b=2"; - getParameter(name):返回请求参数,GET请求从URL读取参数,POST请求从Body中读取参数;
- getContentType():获取请求Body的类型,例如,
"application/x-www-form-urlencoded"; - getContextPath():获取当前Webapp挂载的路径,对于ROOT来说,总是返回空字符串
""; - getCookies():返回请求携带的所有Cookie;
- getHeader(name):获取指定的Header,对Header名称不区分大小写;
- getHeaderNames():返回所有Header名称;
- getInputStream():如果该请求带有HTTP Body,该方法将打开一个输入流用于读取Body;
- getReader():和getInputStream()类似,但打开的是Reader;
- getRemoteAddr():返回客户端的IP地址;
- getScheme():返回协议类型,例如,
"http","https";
HttpServletResponse
封装的响应报文头。常用方法:
- setStatus(sc):设置响应代码,默认是
200; - setContentType(type):设置Body的类型,例如,
"text/html"; - setCharacterEncoding(charset):设置字符编码,例如,
"UTF-8"; - setHeader(name, value):设置一个Header的值;
- addCookie(cookie):给响应添加一个Cookie;
- addHeader(name, value):给响应添加一个Header,因为HTTP协议允许有多个相同的Header;
注意:返回报文写入完毕后,不能用close(),要用flush()
多线程实现
一个Servlet类在服务器中只有一个实例,但对于每个HTTP请求,Web服务器会使用多线程执行请求。因此,一个Servlet的
doGet()、doPost()等处理请求的方法是多线程并发执行的。如果Servlet中定义了字段,要注意多线程并发访问的问题
重定向 Redirect
指的是服务器返回一个重定向指令,告诉浏览器地址已变,需要用新的url发送请求。虽然在用户的角度,是会自动跳转的(浏览器自动解析响应的location字段,并向新的url发送请求)。但是实际上浏览器请求了两次。
具体来说:
- 服务端在处理方法中,返回重定向响应
|
|
2.浏览器接收到类似响应
|
|
3.浏览器按照Location的新url发送新请求
重定向种类:
- 永久重定向 301 (浏览器会缓存新旧url的关联,下次会直接请求到新的url)
- 临时重定向 302
转发 Forward
指的是服务器内部转发。Servlet A 可以把请求转发给Servlet B来处理。与重定向的区别是,这种方式浏览器实际上只请求了一次。浏览器的地址栏上仍然是第一次请求的路径,也就是说浏览器其实并不知道发生了转发。
Session和Cookie
Session
基于唯一ID识别用户身份的机制称为Session。每个用户第一次访问服务器后,会自动获得一个Session ID。如果用户在一段时间内没有访问服务器,那么Session会自动失效。在Servlet中,可以使用HttpSession对象来维护。常见的场景就是记住用户登录后的身份。
使用Session时,由于服务器把所有用户的Session都存储在内存中,如果遇到内存不足的情况,就需要把部分不活动的Session序列化到磁盘上,这会大大降低服务器的运行效率,因此,放入Session的对象要小,通常我们放入一个简单的
User对象就足够了
Cookie
Cookie是一种用于在客户端(通常是Web浏览器)和服务器之间传递数据的小型文本文件。Cookie会被存储在用户的浏览器中,每次用户访问相关站点时,浏览器会将相应的Cookie信息包含在HTTP请求中发送到服务器。在Servlet中,可以使用Cookie对象来维护。
cookie的唯一性:Cookie的唯一性是由域名和名称共同决定的。在同一个域名下,每个Cookie的名称必须是唯一的。这意味着无论在哪个路径下创建,具有相同名称的Cookie都会被视为同一个Cookie。
举个例子,假设你在域名为"example.com"下创建了两个Cookie:
- 名称:“username”,路径:"/"
- 名称:“username”,路径:"/app"
尽管这两个Cookie的路径属性不同,但由于它们具有相同的名称和相同的域名,浏览器会将它们视为同一个Cookie。因此,在请求"/“路径下的资源时,浏览器会发送这两个Cookie,即使它们在不同的路径下创建。
这种特性使得在同一域名下的不同路径之间共享Cookie变得可能,但也需要注意确保不同路径下的Cookie名称不发生冲突,以避免不必要的问题。
JSP(Java Server Pages)
JSP的文件必须放到/src/main/webapp下,文件名以.jsp结尾。JSP和Servlet其实没有任何区别,因为JSP在执行前首先被编译成一个Servlet。
整个文件与HTML并无太大区别,但需要插入变量,或者动态输出的地方,使用特殊指令
<% ... %>。
JSP页面内置了几个变量:
- out:表示HttpServletResponse的PrintWriter;
- session:表示当前HttpSession对象;
- request:表示HttpServletRequest对象。
MVC开发
初级阶段
MVC(Model-View-Controller) 设计模式 可以结合 Servlet和JSP 的优点。Servlet和JSP对比:
- Servlet擅长逻辑处理,但是不适合复杂的HTML处理
- JSP则相反
廖老师举了一个例子:
- 一个JavaBean对象,为User对象
- 一个Servlet对象,负责从数据库读取具体的user信息,并把读取到的JavaBean放到HttpServletRequest中,再通过
forward()传给user.jsp处理 - 在user.jsp中,只负责展示获取到的JavaBean的信息。不需要处理复杂的逻辑。
也就是:在浏览器访问http://localhost:8080/user,请求首先由UserServlet处理,然后交给user.jsp渲染。在这个例子中:
- Model = User对象
- View = user.jsp
- Controller = Servlet
进阶阶段
Spring MVC , 后面会学到
Filter 过滤器
在请求到达具体Servlet之前,可能还需要一些公共的处理逻辑等。因此,JavaEE的Servlet规范还提供了一组Filter组建。
它的作用是,在HTTP请求到达Servlet之前,可以被一个或多个Filter预处理,类似打印日志、登录检查等逻辑,完全可以放到Filter中。
具体的代码例子:
|
|
@WebFilter注解表示了要过滤的URL。这种过滤类都需要implements Filter。并在doFilter中实现处理。要继续处理请求,必须调用chain.doFilter()(因此也可以不调用此方法实现中断后续的处理)。
修改请求和响应
有时候用到过滤器时,需要修改请求和响应对象。
Listener 监听器
Servlet定义了好几种监听器,其中最常用的是ServletContextListener。示例代码如下:
|
|
任何标注为
@WebListener,且实现了特定接口的类会被Web服务器自动初始化。上述AppListener实现了ServletContextListener接口,它会在整个Web应用程序初始化完成后,以及Web应用程序关闭后获得回调通知。我们可以把初始化数据库连接池等工作放到contextInitialized()回调方法中,把清理资源的工作放到contextDestroyed()回调方法中,因为Web服务器保证在contextInitialized()执行后,才会接受用户的HTTP请求。
关于部署
一般一个具体的Web应用结构如下:
|
|
部署架构一般如下:
|
|
实现上述功能的Nginx配置文件如下:
|
|