欢迎来到Servlet详解! 在现代Web开发中,Servlets扮演着至关重要的角色,为我们打开了创建动态和交互式Web应用的大门。作为Java在服务器端的强大工具,Servlets不仅提供了灵活的处理HTTP请求和响应的能力,还可以实现各种功能,如表单处理、会话管理、数据库连接等。正是通过Servlets,我们可以构建出功能丰富、高效、可扩展的Web应用程序。在本文中,我们将带您深入探索Servlets的魅力,揭示其背后的机制和技术,以及如何最大限度地利用它们来实现卓越的Web开发。无论您是初学者还是有经验的开发人员,本文都将为您提供深入的洞见和实用的技巧,帮助您在Web开发的道路上迈出坚实的步伐。准备好了吗?让我们一起展开这段令人兴奋的Servlet之旅吧!
简介
Servlet是Server Applet的简称,称为服务端小程序,是JavaEE平台下的技术标准,基于Java语言编写的服务端程序
Web 容器或应用服务器实现了Servlet标准所以Servlet需要运行在Web容器或应用服务器中
Servlet主要功能在于能够在服务器中执行并生成数据
特点
采用单进程多线程的方式运行
介于数据库和web服务之间
作用
获取用户提交的数据
获取浏览器附加的信息
处理数据(访问数据库或调用接口)
给浏览器产生一个响应
在响应中添加附加信息
生命周期
init():初始化
service():服务
destroy():销毁
当客户端浏览器第一次请求Servlet时,容器会实例化这个Servlet,然后调用一次init方法,并在新的线程中执行service方法处理请求。
service方法执行完毕后容器不会销毁这个Servlet而是做缓存处理,当客户端浏览器再次请求这个Servlet时,
容器会从缓存中直接找到这个Servlet对象,并再一次在新的线程中执行Service方法
当容器在销毁Servlet之前对调用一次destroy方法
处理请求的原理
当浏览器基于get方式请求我们创建Servlet时,我们自定义的Servlet中的doGet方法会被执行。
doGet方法能够被执行并处理get请求的原因是,容器在启动时会解析web工程中WEB-INF目录中的web.xml文件,
在该文件中我们配置了Servlet与URI的绑定,容器通过对请求的解析可以获取请求资源的URI,
然后找到与该URI绑定的Servlet并做实例化处理(注意:只实例化一次,如果在缓存中能够找到这个Servlet就不会再做次实例化处理)。
在实例化时会使用Servlet接口类型作为引用类型的定义,并调用一次init方法,
由于GenericServlet中重写了该方法所以最终执行的是GenericServlet中init方法(GenericServlet中的Init方法是一个空的方法体),
然后在新的线程中调用service方法。由于在HttpServlet中重写了Service方法所以最终执行的是HttpServlet中的service方法。
在service方法中通过request.getMethod()获取到请求方式进行判断如果是Get方式请求就执行doGet方法,如果是POST请求就执行doPost方法。
如果是基于GET方式提交的,并且在我们的Servlet中又重写了HttpServlet中的doGet方法,那么最终会根据Java的多态特性转而执行我们自定义的Servlet中的doGet方法。
Servlet接口
init(),创建Servlet对象后立即调用该方法完成一些初始化工作。
service(),处理客户端请求,执行业务操作,利用响应对象响应客户端请求。
destroy(),在销毁Servlet对象之前调用该方法,释放资源。
getServletConfig(),ServletConfig是容器向servlet传递参数的载体。
getServletInfo(),获取servlet相关信息
ServletConfig接口
String getServletName(),返回 Servlet 的名字,即 web.xml 中 元素的值。
ServletContext getServletContext(),返回一个代表当前 Web 应用的 ServletContext 对象。
String getInitParameter(String name),根据初始化参数名返回对应的初始化参数值。
Enumeration getInitParameterNames(),返回一个 Enumeration 对象,其中包含了所有的初始化参数名。
GenericServle抽象类
GenericServlet是实现了Servlet接口的抽象类
在GenericServlet中进一步的定义了Servlet接口的具体实现,其设计的目的是为了和应用层协议解耦,在GenericServlet中包含一个Service抽象方法
HttpServlet类
继承自 GenericServlet,针对于处理 HTTP 协议的请求所定制
在 HttpServlet的service()方法中已经把 ServletReuqest 和 ServletResponse 转为 HttpServletRequest 和 HttpServletResponse
直接使用 HttpServletRequest 和 HttpServletResponse, 不再需要强转
实际开发中, 直接继承 HttpServlet, 并根据请求方式复写 doXxx() 方法即可
简介
HttpServletRequest对象代表客户端浏览器的请求,当客户端浏览器通过HTTP协议访问服务器时,
HTTP请求中的所有信息都会被Tomcat所解析并封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息
生命周期
当有请求到达Tomcat时,Tomcat会创建HttpServletRequest对象,
并将该对象通过参数的方式传递到我们Servlet的方法中,
当处理请求处理完毕并产生响应后该对象生命周期结束
获取请求信息
req.getRequestURL()返回客户端浏览器发出请求时的完整URL。
req.getRequestURI()返回请求行中指定资源部分。
req.getRemoteAddr()返回发出请求的客户机的IP地址。
req.getLocalAddr()返回WEB服务器的IP地址。
req.getLocalPort()返回WEB服务器处理Http协议的连接器所监听的端口。
获取表单数据
req.getParameter("key");根据key获取对应的value,返回一个字符串
获取复选框的数据
req.getParameterValues("checkboxkey");获取复选框(checkbox组件)中的值,返回一个字符串数组
String[] userlikes = req.getParameterValues("checkboxkey");
获取所有提交数据的Key
req.getParameterNames()获取请求中所有数据的key,该方法返回一个枚举类型
Enumeration
使用Map结构获取提交数据
req.getParameterMap()获取请求中所有的数据并存放到一个Map结构中,该方法返回一个Map,其中key为String类型value为String[]类型
Map
设置请求编码
req.setCharacterEncoding("utf-8")
请求的数据包基于字节在网络上传输,Tomcat接收到请求的数据包后会将数据包中的字节转换为字符。
在Tomcat中使用的是ISO-8859-1的单字节编码完成字节与字符的转换,所以数据中含有中文就会出现乱码,
可以通过req.setCharacterEncoding("utf-8")方法来对提交的数据根据指定的编码方式重新做编码处理
获取请求头信息
req.getHeader("headerKey")根据请求头中的key获取对应的value
String headerValue = req.getHeader("headerKey");
req.getHeaderNames()获取请求头中所有的key,该方法返回枚举类型
Enumeration
请求转发
请求转发是服务端的一种请求方式,相当于在服务端中直接请求某个资源
request.getRequestDispatcher("/test.jsp").forword(request,response);
简介
HttpServletResponse对象代表服务器的响应。
这个对象中封装了响应客户端浏览器的流对象,以及向客户端浏览器响应的响应头、响应数据、响应状态码等信息
设置响应类型
resp.setContentType("MIME")该方法可通过MIME-Type设置响应类型
resp.setContentType("text/html")设置响应类型为文本型,内容含有html字符串,是默认的响应类型
resp.setContentType("text/plain")设置响应类型为文本型,内容是普通文本。
resp.setContentType("application/json")设置响应类型为JSON格式的字符串。
resp.setContentType("image/jpeg")设置响应类型为图片类型,图片类型为jpeg或jpg格式。
resp.setContentType("image/gif")设置响应类型为图片类型,图片类型为gif格式。
设置响应编码
response.setContentType("text/html; charset=UTF-8");
response.setCharacterEncoding("UTF-8");
重定向响应
response.sendRedirect(URL地址)
重定向会产生两次请求两次响应。
重定向的URL是由客户端浏览器发送的。
浏览器地址栏会有变化
请求转发与重定向的区别
请求转发对于客户端浏览器而言是在一次请求与响应中完成,而重定向是在两次请求两次响应中完成。
请求转发并不会改变客户端浏览器的地址栏中的内容。而重定向会改变客户端浏览器地址栏中的内容。
请求转发可以使用request对象传递数据,而重定向不能使用request对象传递数据。
如果是处理的DML操作,建议使用重定向方式为客户端浏览器产生响应,可以解决表单重复提交现象
简介
ServletContext官方叫Servlet上下文。服务器会为每一个Web应用创建一个ServletContext对象
这个对象全局唯一,而且Web应用中的所有Servlet都共享这个对象。所以叫全局应用程序共享对象
生命周期
当容器启动时会创建ServletContext对象并一直缓存该对象,直到容器关闭后该对象生命周期结束。
ServletContext对象的生命周期非常长,所以在使用全局容器时不建议存放业务数据
相对路径转绝对路径
context.getRealPath("path")该方法可以将一个相对路径转换为绝对路径,在文件上传与下载时需要用到该方法做路径的转换
获取容器的附加信息
servletContext.getServerInfo()返回Servlet容器的名称和版本号
servletContext.getMajorVersion()返回Servlet容器所支持Servlet的主版本号。
servletContext.getMinorVersion()返回Servlet容器所支持Servlet的副版本号。
获取web.xml文件中的信息
servletContext.getInitParameter("key")该方法可以读取web.xml文件中标签中的配置信息。
servletContext.getInitParameterNames()该方法可以读取web.xml文件中所有param-name标签中的值。
全局容器
servletContext.setAttribute("key",ObjectValue)向全局容器中存放数据。
servletContext.getAttribute("key")从全局容器中获取数据。
servletContext.removeAttribute("key")根据key删除全局容器中的value。
简介
ServletConfig对象对应web.xml文件中的节点。当Tomcat初始化一个Servlet时,
会将该Servlet的配置信息,封装到一个ServletConfig对象中
我们可以通过该对象读取节点中的配置信息
简介
servletConfig.getInitParameter("key")该方法可以读取web.xml文件中标签中标签中的配置信息。
servletConfig.getInitParameterNames()该方法可以读取web.xml文件中当前标签中所有标签中的值
特点
Cookie使用字符串存储数据
Cookie使用Key与Value结构存储数据
单个Cookie存储数据大小限制在4097个字节
Cookie存储的数据中不支持中文,Servlet4.0中支持
Cookie是与域名绑定所以不支持跨一级域名访问
Cookie对象保存在客户端浏览器内存或系统磁盘中
Cookie分为持久化Cooke与状态Cookie
浏览器在保存同一域名所返回Cookie的数量是有限的。不同浏览器支持的数量不同,Chrome浏览器为50个
浏览器每次请求时都会把与当前访问的域名相关的Cookie在请求中提交到服务端
Cookie对象的创建
Cookie cookie = new Cookie("key","value")通过new关键字创建Cookie对象
response.addCookie(cookie)通过HttpServletResponse对象将Cookie写回给客户端浏览器。
获取Cookie中的数据
浏览器每次请求时都会把与当前访问的域名相关的Cookie在请求中提交到服务端
通过HttpServletRequest对象获取Cookie,返回Cookie数组。
Cookie[] cookies = request.getCookies()
解决Cookie不支持中文
在Cookie中name的值不能使用中文,Value是可以的【Servlet4.0版本之前不支持】
如果存储的数据中含有中文,代码会直接出现异常
我们可以通过对含有中文的数据重新进行编码来解决该问题
URLEncoder.encode("content","code")将内容按照指定的编码方式做URL编码处理。
URLDecoder.decode("content","code")将内容按照指定的编码方式做URL解码处理
Cookie跨域问题
Cookie不支持一级域名的跨域,支持二级域名的跨域
例如:baidu.com 为一级域名,news.baidu.com为二级域名
状态Cookie与持久化Cookie
状态Cookie:Cookie对象仅会被缓存在浏览器所在的内存中。当浏览器关闭后Cookie对象 也会被销毁。
持久化Cookie:浏览器会对Cookie做持久化处理,基于文件形式保存在系统的指定目录中。在Windows10系统中为了安全问题不会显示Cookie中的内容。
当Cookie对象创建后默认为状态Cookie。可以使用Cookie对象下的cookie.setMaxAge(60)方法设置失效时间,单位为秒
一旦设置了失效时间,那么该Cookie为持久化Cookie,浏览器会将Cookie对象持久化到磁盘中。当失效时间到达后文件删除
特点
HttpSession保存在服务端
HttpSession使用Key与Value结构存储数据
HttpSession的Key是字符串类型,Value则是Object类型
HttpSession存储数据大小无限制
生命周期
在HttpSession对象生命周期中没有固定的创建时间与销毁时间
何时创建取决于我们什么时候第一次调用了getSession()或getSession(true)的方法
HttpSession对象的销毁时间取决于超时时间的到达以及调用了invalidate()方法
如果没有超时或者没有调用invalidate()方法,那么HttpSession会一直存储
默认超时时间为30分钟(Tomcat的web.xml文件配置的时间就是默认超时时间)。
HttpSession对象的创建
HttpSession对象的创建是通过request.getSession()方法来创建的
客户端浏览器在请求服务端资源时,如果在请求中没有jsessionid,
getSession()方法将会为这个客户端浏览器创建一个新的HttpSession对象,
并为这个HttpSession对象生成一个jsessionid,在响应中通过状态Cookie写回给客户端浏览器,
如果在请求中包含了jsessionid,getSession()方法则根据这个ID返回与这个客户端浏览器对应的HttpSession对象。
getSession()方法还有一个重载方法getSession(true|false)。当参数为true时与getSession()方法作用相同。
当参数为false时则只去根据jsessionid查找是否有与这个客户端浏览器对应的HttpSession,如果有则返回,
如果没有jsessionid则不会创建新的HttpSession对象
HttpSession对象的使用
session.setAttribute("key",value)将数据存储到HttpSession对象中
Object value = session.getAttribute("key")根据key获取HttpSession中的数据,返回Object
Enumeration attributeNames = session.getAttributeNames()获取HttpSession中所有的key,返回枚举类型
session.removeAttribute("key")根据key删除HttpSession中的数据
String id = session.getId()根据获取当前HttpSession的SessionID,返回字符串类型
HttpSession的销毁方式
通过web.xml文件指定超时时间
通过HttpSession对象中的invalidate()方法销毁当前HttpSession对象
简介
在Servlet3.0之前的版本中如果实现文件上传需要依赖apache的Fileupload组件
在Servlet3.0以及之后的版本中提供了Part对象处理文件上传,所以不在需要额外的添加Fileupload组件。
在Servlet3.0以及之后的版本中实现文件上传时必须要在Servlet中开启多参数配置
web.xml
@MultipartConfig
fileSizeThreshold int 当数据量大于该值时,内容将被写入临时文件。
location String 存放生临时成的文件地址
maxFileSize long 允许上传的文件最大值(byte)。默认值为 -1,表示没有限制
maxRequestSize long 一个 multipart/form-data请求能携带的最大字节数(byte),默认值为 -1,表示没有限制。
Part对象中常用的方法
long getSize()上传文件的大小
String getSubmittedFileName()上传文件的原始文件名
String getName()获取input name="upload" ...>标签中name属性值
InputStream getInputStream()获取上传文件的输入流
void write(String path)保存文件至服务器
在实现文件下载时,我们需要在响应头中添加附加信息
response.addHeader("Content-Disposition", "attachment; filename="+文件名);
Content-Disposition:attachment该附加信息表示作为对下载文件的一个标识字段。不会在浏览器中显示而是直接做下载处理。
filename=文件名表示指定下载文件的文件名
解决文件名中文乱码问题
resp.addHeader("Content-Disposition","attachment;filename="+new String(file.getName().getBytes("gbk"),"iso-8859-1"));
@WebServlet
value String[] Servlet的访问URL,支持多个
initParams WebInitParam[] Servlet的init参数
name String Servlet的名称
urlPatterns String[] Servlet的访问URL,支持多个
loadOnStartup int 自启动Servlet
description String Servlet的描述
displayName String Servlet的显示名称
asyncSupported boolean 声明Servlet是否支持异步操作模式
@WebInitParam
name String param-name
value String param-value
description String description
简介
Filter过滤器是Servlet2.3中所提供的一个过滤请求与响应的对象
Filter过滤器既可以对客户端向服务器端发送的请求进行过滤
也可以对服务器端向客户端产生的响应进行过滤处理
生命周期
Filter的生命周期是由容器管理的。当容器启动时会实例化Filter并调用init方法完成初始化动作。
当客户端浏览器发送请求时,容器会启动一个新的线程来处理请求,如果请求的URL能够被过滤器所匹配,
那么则先调用过滤器中 的doFilter方法,再根据是否有chain.doFilter的指令,
决定是否继续请求目标资源。当容器关闭时会销毁Filter对象,在销毁之前会调用destroy方法
设计模式
在Servlet的Filter中使用的责任链设计模式。
责任链模式特点
责任链(Chain of Responsibility):责任链模式也叫职责链模式,是一种对象行为模式。
在责任链模式里,很多对象由每一个对象对其下一个对象的引用而连接起来形成一条链。
请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不需要知道链上的哪一个对象最终处理这个请求,
这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。
责任链的优缺点
优点:
降低了对象之间的耦合度。
增强了系统的可扩展性。
增强了给对象指派职责的灵活性。
责任链简化了对象之间的连接。
责任分担。每个类只需要处理自己该处理的工作。
缺点:
不能保证请求一定被接收。
对比较长的责任链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
可能会由于责任链的错误设置而导致系统出错,如可能会造成循环调用。
Filter对象的创建
创建一个Class实现Filter接口,并实现接口中三个抽象方法
init()方法:初始化方法,在创建Filter后立即调用。可用于完成初始化动作。
doFilter()方法:拦截请求与响应方法,可用于对请求和响应实现预处理。
destroy()方法:销毁方法,在销毁Filter之前自动调用。可用于完成资源释放等动作
FilterConfig对象的使用
FilterConfig对象是用来读取中初始化参数的对象
该对象通过参数传递到init方法中,用于读取初始化参数。
filterConfig.getInitParameter("name")通过name获取对应的value。
filterConfig.getInitParameterNames()返回该Filter中所有中的值
简介
Filter技术的特点是在对请求或响应做预处理时,可实现“插拔式”的程序设计
我们可以根据自己需求添加多个Filter,也可以根据需求去掉某个Filter,通过修改web.xml文件即可实现
那么如果有多个过滤器对某个请求及响应进行过滤,那么这组过滤器就称为过滤器链
执行顺序
按照在web.xml文件中配置的上下顺序来决定先后
简介
Filter支持注解式开发,通过@WebFilter注解替代web.xml中Filter的配置
@WebFilter
使用注解式开发Filter时,执行顺序会根据Filter的名称进行排序的结果决定调用的顺序
filterName String 指定过滤器的 name 属性
urlPatterns String[] 拦截请求的URL,支持多个
value String[] 拦截请求的URL,支持多个
description String 过滤器的描述
displayName String 过滤器的显示名称
initParams WebInitParam[] 指定一组过滤器初始化参数,等价于 标签。
简介
监听器用于监听web应用中某些对象的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理
当范围对象的状态发生变化的时候,服务器会自动调用监听器对象中的方法
分类
ServletContext对象生命周期监听器与属性操作监听器;
HttpSession对象生命周期监听器与属性操作监听器;
ServletRequest对象生命周期监听器与属性操作监听器;
设计模式
在Servlet的Listener中使用的观察者设计模式。
观察者模式的特点
观察者模式(Observer Pattern):观察者模式是一种对象行为模式。
它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式的优缺点
优点:
观察者和被观察者是抽象耦合的。
建立一套触发机制。
缺点:
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
ServletContext对象的生命周期监听器
ServletContextListener接口定义了ServletContext对象生命周期的监听行为。
void contextInitialized(ServletContextEvent sce)
ServletContext对象创建之后会触发该监听方法,并将ServletContext对象传递到该方法中。
void contextDestroyed(ServletContextEvent sce)
ServletContext对象在销毁之前会触发该监听方法,并将ServletContext对象传递到该方法中。
ServletContext对象的属性操作监听器
ServletContextAttributeListener接口定义了对于ServletContext对象属性操作的监听行为。
void attributeAdded(ServletContextAttributeEvent scae)
向ServletContext对象中添加属性时会触发该监听方法,并将ServletContext对象传递到该方法中。
触发事件的方法servletContext.setAttribute("key","value")。
void attributeRemoved(ServletContextAttributeEvent scae)
当从ServletContext对象中删除属性时会触发该监听方法,并将ServletContext对象传递到该方法中。
触发事件方法servletContext.removeAttribute("key")。
void attributeReplaced(ServletContextAttributeEvent scae)
当从ServletContext对象中属性的值发生替换时会触发该监听方法,并将ServletContext对象传递到该方法中。
触发事件的方法servletContext.setAttribute("key","value")。
HttpSession对象的生命周期监听器
HttpSessionListener接口定义了HttpSession对象生命周期的监听行为。
void sessionCreated(HttpSessionEvent se)
HttpSession对象创建后会触发该监听方法,并将已创建HttpSession对象传递到该方法中。
void sessionDestroyed(HttpSessionEvent se)
HttpSession对象在销毁之前会触发该监听方法,并将要销毁的HttpSession对象传递到该方法中
HttpSession对象的属性操作监听器
HttpSessionAttributeListener接口定义了对于HttpSession对象属性操作的监听行为。
void attributeAdded(HttpSessionBindingEvent se)
向HttpSession对象中添加属性时会触发该监听方法,并将HttpSession对象传递到该方法中。
触发事件的方法HttpSession.setAttribute("key","value")。
void attributeRemoved(HttpSessionBindingEvent se)
当从HttpSession对象中删除属性时会触发该监听方法,并将HttpSession对象传递到该方法中。
触发事件方法HttpSession.removeAttribute("key")。
void attributeReplaced(HttpSessionBindingEvent se)
当从HttpSession对象中属性的值发生替换时会触发该监听方法,并将HttpSession对象传递到该方法中。
触发事件的方法HttpSession.setAttribute("key","value")。
HttpServletRequest对象的生命周期监听器
ServletRequestListener接口定义了ServletRequest(是HttpServletRequest接口的父接口类型)对象生命周期的监听行为。
void requestInitialized(ServletRequestEvent sre)
HttpServletRequest对象创建后会触发该监听方法,并将已创建HttpServletRequest对象传递到该方法中。
void requestDestroyed(ServletRequestEvent sre)
HttpServletRequest对象在销毁之前会触发该监听方法,并将要销毁HttpServletRequest对象传递到该方法中。
HttpServletRequest对象的属性操作监听器
ServletRequestAttributeListener接口定义了对于HttpServletRequest对象属性操作的监听行为。
void attributeAdded(ServletRequestAttributeEvent srae)
向HttpServletRequest对象中添加属性时会触发该监听方法,并将HttpServletRequest对象传递到该方法中
触发事件的方法HttpServletRequest.setAttribute("key","value")。
void attributeRemoved(ServletRequestAttributeEvent srae)
当从HttpServletRequest对象中删除属性时会触发该监听方法,并将HttpServletRequest对象传递到该方法中。
触发事件方法HttpServletRequest.removeAttribute("key")。
void attributeReplaced(ServletRequestAttributeEvent srae)
当从HttpServletRequest对象中属性的值发生替换时会触发该监听方法,并将HttpServletRequest对象传递到该方法中。
触发事件的方法HttpServletRequest.setAttribute("key","value")。
简介
Listener支持注解式开发,通过@WebListener注解替代web.xml中Listener的配置