Web基础之Servlet
Servlet
Servlet : server applet,直译服务小程序。那Servlet到底是什么呢?
Servlet说白了其实就是一个接口,接口的作用是什么?规范呗,这个接口规定了下面三个问题:
- 初始化时做什么
- 接收到请求时做什么
- 销毁时做什么
因此在Servlet中只添加业务逻辑,具体如何监听、用什么端口等由容器(如Tomcat)完成,servlet不关心也不需要关心。
Servlet在容器中的配置
在Tomcat中有两种方式配置Servlet,一种是在xml中配置,另一种是使用注解:
Xml配置Servlet:
<!--配置Servlet的类路径-->
<servlet>
<servlet-name>DemoServlet</servlet-name><!--servlet名-->
<servlet-class>com.heima.servlet.DemoServlet</servlet-class><!--类全名-->
</servlet>
<!--配置Servlet的映射路径-->
<servlet-mapping>
<servlet-name>DemoServlet</servlet-name><!--映射Servlet名-->
<url-pattern>/demoServlet</url-pattern><!--访问路径-->
</servlet-mapping>
servlet 3.0 注解配置:
//直接在类名上使用注解:
@WebServlet(name = "Servlet", urlPatterns = "/1")
//或者省略模式:
@WebServlet("/1")//注解默认赋值给value,即urlPatterns
xml与注解优缺点分析:
xml优缺点:
- 优点:耦合性低,容易编辑,配置比较集中,方便修改,在大业务量的系统里面,通过xml配置会方便后人理解整个系统的架构
- 缺点:比较繁琐,类型不安全,配置形态丑陋,配置文件过多的时候难以管理,并且IDE不能检测配置的正确性
注解优缺点:
- 优点:方便,简洁,配置信息和 Java 代码放在一起,有助于增强程序的内聚性。
- 耦合性高,分散到各个class文件中,所以不宜维护
xml与注解应该通过合适的场景进行选择,快速开发时优先选用注解。
Servlet生命周期
Servlet对象在第一次被访问时由Tomcat容器创建,并且一个Servlet对象只会被创建一次,创建时调用init方法,在Tomcat关闭时销毁,销毁时调用destory方法。
Servlet可以配置为在Tomcat启动时直接创建,即在xml中添加<load-on-startup>number</load-on-startup>
number的大小为优先级:
- number取值小于0或不配置:被访问时才去加载;
- number取值为0时:最先被加载;
- number取值大于0时:数值越小Servlet越优先加载;
Servlet的实现类还可以在构造方法中说明被创建时做什么
GenericServlet
为了简化Servlet接口,GenericServlet提供了除service方法外的平庸实现。继承GenericServlet后只需要重写service即可。
HTTPServlet
HTTPServlet继承GenericServlet,专门用来处理HTTP请求,可以区分get和post方法,并针对不同的请求作出不同的响应。
简单来说,接口定义了规则,抽象类提供了模板,子类进行具体实现。
路径映射规范
不同的匹配方式均可访问到该Servlet:
匹配方式 | - | 描述 |
---|---|---|
完全匹配 | /user/login | 资源路径为/user/login时可以访问 |
目录匹配 | /user/* | 资源路径中含有/user目录均可访问 |
后缀名匹配 | *.do | 资源路径中以.do结尾的均可访问 |
缺省路径 | / | 访问的路径找不到,就会去找缺省路径 |
缺省时会在项目根目录寻找index.html文件。
开发中一般使用全路径配置。
注解在xml中对应的属性:
属性名 | 类型 | 描述 |
---|---|---|
name | String | 指定Servlet的name属性,等价于<servlet-name> 。如果没有显示指定,则该Servlet的取值即为类的全限定名。 |
value | String[] | 该属性等价于 urlPatterns 属性,两个属性不能同时使用。 |
urlPatterns | String[] | 指定一组Servlet的URL匹配模式,等价于<url-pattern> 标签 |
loadOnStartup | int | 指定Servlet的加载顺序,等价于<load-on-startup> 标签 |
initParams | WebInitParam[] | 指定一组Servlet初始化参数,等价于<init-param> 标签 |
asyncSupported | boolean | 声明Servlet是否支持异步操作模式,等价于<async-supported> 标签 |
description | String | 该Servlet描述信息,等价于<description> 标签 |
displayName | String | 该Servlet的显示名,通常配合工具使用,等价于<display-name> |
HTTP协议
HTTP:无状态的应用层协议。
主要的两种请求方式:
Get:
- 没有请求体
- 提交数据的方式为在URL后拼接:
url?name1=value1&name2=value2
Post:
- 有请求体
- 提交数据方式为在请求体中:
name1=value1&name2=value2
HttpServletRequest
浏览器的请求信息都封装在HttpServletRequest这个对象中。
常用API:
方法 | 说明 |
---|---|
String getMethod() | 获取请求方式的类型 |
StringBuffer getRequestURL() | 获取客户端发出请求完整URL |
String getRemoteAddr() | 获取IP地址 |
String getProtocol() | 获取当前协议的名称和版本 |
String getParameter(String name) | 获取指定name的value值 |
String[] getParameterValues(String name) | 获取指定name的value值数组(比如多选框) |
Map<String, String[]> getParameterMap() | 获取所有的数据封装到map中 |
请求头信息:
方法 | 作用 |
---|---|
String getHeader(String name) | 根据请求头的k关键字获取请求头信息 |
Enumeration getHeaderNames() | 返回此请求包含的所有头信息的枚举 |
getHeader中的关键字:
请求头key | 请求头value |
---|---|
referer | 浏览器通知服务器,当前请求来自何处,如果是直接访问,则不会有这个头。常用于:防盗链 |
If-modified-Since | 浏览器通知服务器,本地缓存的最后变更时间。与另一个响应头组合控制浏览器页面的缓存。 |
cookie | 与会话有关技术,用于存放浏览器缓存的cookie信息。 |
user-agent | 浏览器通知服务器,客户端浏览器与操作系统相关信息 |
connection | 保持连接状态。Keep-Alive 连接中,close 已关闭 |
host | 请求的服务器主机名 |
content-length | 请求体的长度 |
content-type | 如果是POST请求,会有这个头,默认值为application/x-www-form-urlencoded,表示请求体内容使用url编码 |
accept | 浏览器可支持的MIME类型。文件类型的一种描述方式。 |
mime格式 | 浏览器请求数据的类型,例如: text/html ,html文件 text/css,css文件 text/javascript,js文件 image/*,所有图片文件 |
accept-encoding | 浏览器通知服务器,浏览器支持的数据压缩格式。如:GZIP压缩 |
accept-language | 浏览器通知服务器,浏览器支持的语言。各国语言(国际化i18n) |
POST请求提交数据乱码问题:
原因:tomcat接收post请求数据采用的编码是ISO-8859-1,而我们的整个编码环境都是UTF-8。所以会导致乱码。
Servlet中获取数据前,将request编码设置为UTF-8:request.setCharacterEncoding("UTF-8")
;
request生命周期
每次请求都会创建一个新对象,请求完成、响应结束后request和response对象就销毁。
request域
request实现类中使用了一个Map来存储属性:
方法 | 说明 |
---|---|
void setAttribute(String name, Object o) | 往request域中设置值 |
Object getAttribute(String name) | 从request域中取值 |
void removeAttribute(String name) | 从request域中移除值 |
request.getAttribute(name)
:从rquest域对象中取值request.getParameter(name)
:从rquest中获取请求数据
作用范围:一次请求和响应之间。
请求转发
RequestDispatcher getRequestDispatcher(String path)
获取请求转发器(request对象方法)void forward(ServletRequest request, ServletResponse response)
传递request和response对象(转发器方法)
- 转发是服务器内部的跳转行为;
- 从一个Servlet转发到另一个资源(静态或动态),能够实现跳转,但是浏览器地址栏地址没有发生改变。因为对浏览器来说本质上就只有一次请求;
- 请求转发共享request域中的数据;
一个简单的登录功能执行顺序:
类分布:
servlet类接收请求后获取用户名和密码,封装为User并调用UserService类的方法验证登录,UserService方法调用Dao层的UserDao,Dao使用数据库工具类进行查询最后一步步返回。
HttpServletResponse
常见状态码:
状态码 | 状态码描述 | 说明 |
---|---|---|
200 | OK | 请求已成功,请求所希望的响应头或数据体将随此响应返回。出现此状态码是表示正常状态。 |
302 | Move temporarily | 重定向,请求的资源临时从不同的 URI响应请求。 |
304 | Not Modified | 从缓存中读取数据,不从服务器重新获取数据 |
404 | Not Found | 请求资源不存在。通常是用户路径编写错误,也可能是服务器资源已删除。 |
403 | Forbidden | 服务器已经理解请求,但是拒绝执行它 |
405 | Method Not Allowed | 请求行中指定的请求方法不能被用于请求相应的资源 |
500 | Internal Server Error | 服务器内部错误。通常程序抛异常 |
HttpServletResponse常用API:
API | 描述 |
---|---|
setStatues(int code) | 设置状态码(响应行) |
setHeader(key,value) | 设置响应头(响应方式) |
常见响应方式:
响应头Key | 响应头value |
---|---|
refresh | 页面刷新,例如:3;url=www.baidu.com //三秒刷新页面到www.baidu.com |
content-type | 设置响应数据的类型(MIME类型)和编码格式 例如:text/html;charset=UTF-8 text/plain;charset=utf-8 (这个可以解决乱码) |
location | 指定响应的路径,需要与状态码302配合使用,完成重定向。 (value为URL) |
content-disposition | 通知浏览器以附件形式解析正文,例如:attachment;filename=xx.zip 。 |
- 转发和重定向:
【实现原理】转发在服务器内部发送,重定向是服务器操作浏览器发送请求
【请求次数】转发只有一次请求,重定向会有两次请求
【地址栏地址】转发不变,重定向后地址栏地址会变为第二次请求的地址 - 转发和重定向的作用:
【转发】完成跳转,共享request域对象(数据)
【重定向】完成跳转
重定向简写方式:response.sendRedirect("url");
设置响应体:
方法 | 说明 |
---|---|
java.io.PrintWriter getWriter() | 用于向浏览器输出字符数据。【字符流--给浏览器响应数据】 |
ServletOutputStream getOutputStream() | 用于向浏览器输出二进制数据。【字节流--文件下载】 |
ServletContext域对象
ServletContext域的作用范围为整个web项目
常用方法: setAttribute(key, value)
getAttribute(key, value)
removeAttribute(key, value)
getRealPath(fileName)
getMimeType(fileName)
cookie&session
cookie存储在客户端,session存储在服务器,现在一般都是联合使用,使用cookie保存JSESSIONID,然后再服务器查询session。
Cookie
常用方法:
- 构造方法:
Cookie(name,value)
- 获取name值:
getName()
- 获取value值:
getValue()
- 设置cookie值:
setValue(value)
- ========================
- 发送cookie:
response.addCookie(cookie)
- 获取所有cookie:
Cookie[] getCookies = rquest.getCookies()
如果存在中文乱码可以使用java.net包下的URLDecoder类:
//先编码
URLEncoder.encode(cookie,"utf-8");//编码后会得到类似这样的编码值:%E6%9D%8E%E5%9B%9B
//再解码
URLDecoder.decode(cookie,"utf-8");//即可得到正确的值:李四
存活时间:
Cookie默认存活时间为会话级,也就是关闭浏览器后cookie消失。(如果CHrome中 启动时
设置为从上次的继续
则Cookie不会消失)
Cookie到期后会自动删除(浏览器行为)(服务器的时间和时区设置可能影响Cookie,对Chrome、IE而言,Cookie的有效时间段是理解为客户端的时间与服务器端时间的间隔,对于FireFox而言,Cookie的有效时间完全由服务器端的时间决定)
可以通过void setMaxAge(int seconds)
设置cookie的最大存活时间。默认为-1
,即会话结束后消失(关闭浏览器),设置为0时立即删除cookie。
有效路径
cookie可以通过setPath(String path)
设置cookie的有效路径,设置过后,在访问该路径及其子路径的时候会带上该cookie。一般情况下设置有效路径为/
或者不设置
删除Cookie
删除需要设置值为空:""
,设置最大存活时间为0,设置有效路径为原路径,然后在响应中添加这个Cookie。
session
常用API:
方法 | 使用示例 | 说明 |
---|---|---|
getSession() | request.getSession() | 获取当前session |
void setAttribute(String name,Object value) | session.setAttribute("loginUser",user) | 将一个对象与一个名称关联之后存储到session中 |
Object getAttribute( String name) | session.getAttribute("loginUser") | 通过名称获取session中的数据 |
void removeAttribute(String name) | session.removeAttribute("loginUser") | 根据指定名称删除session中的数据 |
String getId() | String sessionId = session.getId() | 获取session的id |
invalidate() | ression.invalidate() | 使当前session失效 |
session在第一次调用getSession()
时由Tomcat创建,并存在三种销毁方式:
- session手动销毁:
session.invilidate()
; - 过期销毁:session默认存活时间--30min
- 非正常关闭tomcat
Servlet很重要,但本文都只是些皮毛而已
更新Filter、Listener
Web基础之Filter、Listener
Filter过滤器
作用:对请求和响应进行拦截、放行、增强
接口方法:
destroy()
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
init(FilterConfig filterConfig)
分别是初始化、过滤、销毁。
使用XML配置的过滤器执行顺序按照XML从上到下,使用注解配置的过滤器按照实现了的类名顺序。
执行顺序:过滤器1前 -> 过滤器2前 -> Servlet -> 过滤器2后 -> 过滤器1后
过滤器生命周期和Servlet有些不一样:
- 当Web应用被加载时,Filter被Tomcat创建
- 当Web应用被卸载时,Filter被Tomcat销毁
- Filter只被创建一次,Filter线程不安全
Listener监听器
三种作用域:
作用域 | 接口名 | 作用范围 | 生命周期 |
---|---|---|---|
请求域 | HttpServletRequest | 一个用户的一次请求 | 在服务器接收到HTTP请求时就创建了<br/>在服务器处理结束HTTP请求时就被销毁了 |
会话域 | HttpSession | 一个用户的所有请求 | 在第一用户请求服务器时被创建<br/>服务器关闭、Session过期、手动销毁被销毁 |
上下文域 | ServletContext | 所有用户的所有请求 | 在服务器加载时/启动时被创建<br/>在服务器卸载时/关闭时被销毁 |
八大监听器:
Servlet context events
Lifecycle(作用域监听) *
上下文域创建和销毁
javax.servlet.ServletContextListener
Changes to attributes(作用域内属性监听)*
上下文域属性增删改的操作
javax.servlet.ServletContextAttributeListener
HTTP session events
Lifecycle
会话域创建和销毁
javax.servlet.http.HttpSessionListener
Changes to attributes
会话域属性增删改的操作
javax.servlet.http.HttpSessionAttributeListener
Session migration
javax.servlet.http.HttpSessionActivationListener
钝化: 使对象随Session存储到硬盘中 活化:使对象随Session活化到内存中
Object binding
javax.servlet.http.HttpSessionBindingListener
使对象感知自己被放到session中,称为绑定setAttribute("user", new User()) 使对象感知自己从session中移除,称为解绑removeAttribute("user")
Servlet request events
Lifecycle
请求域创建和销毁
javax.servlet.ServletRequestListener
Changes to attributes
请求域属性增删改的操作
javax.servlet.ServletRequestAttributeListener