Servlet
摘要
主要记录Servlet核心部分知识点: 生命周期,Requst对象和Response对象等
问题列表
Servlet的生命周期
1 Servlet接口的实现类只能有Http服务器负责为我们创建;开发者不能自己创建Servlet接口实现类的的对象,
并且Http服务器默认情况下是在 **当我们调用这个Servlet的时候才回去创建这个Servlet接口的实现类 **的;
为了解决上面那种问题其实我们可以在web.xml中配置来告诉Http服务器在启动的时候就开始创建这个Servlet接口实现类 的实例对象;
<servlet> <servlet-name>oneServlet</servlet-name> <servlet-class>show.mrkay.OneServlet</servlet-class> <!--大于0默认启动时创建--> <load-on-startup>1</load-on-startup> </servlet>
2 Servlet接口实现类在Http服务器运行期间只会创建一个,所以它是单例的;
3 在Http服务器关闭时,Servlet的生命周期也就结束了,此时所以的Servlet就会被销毁;
1) 问题1
get请求到servlet在使用Request获取请求参数时,如果请求参数中有中文,的得到请求参数没有乱码;
post请求到servlet使用Request对象获取请求参数的时候,得到的请求参数没有中文乱;
原因:
get请求参数在请求头,http请求包到达http服务器之后首先对请求头中的数据进行解码,请求头中的二进制内容有Tomcat进行解码,Tomcat9.0使用的utf-8字符集进行解码,所以不会出现中文乱码;
post请求解码对请求体中的参数进行解码的时候,使用Request对象进行解码,Request请求对象默认使用的是ISO-8859-1字符集,所以会出现乱码;
解决:
post请求乱码解决方式: 可以使用request.setCharacterEncoding(“UTF-8”);之后在获取请求参数即可;
2) 问题2
Request和Response对象的生命周期?
分析:
1 当我们的Http服务器接收到浏览器的http请求协议包之后,自动为当前的Http请求协议包创建一个Request请求对象和Response响应对象传入到,doGet和doPost方法中;
2 在Http服务器要将响应包中的数据推送给浏览器的时候,Http服务器会将Request请求对象和Responst响应对象销毁;
请求对象和响应对象贯穿了一次请求的处理过程;
3) 问题3
Http状态码?
分析:
三个数据组成的状态码,http服务器推送响应包之前,将状态码写入到响应包的状态行中,用于通知浏览器如何解析处理这个响应包内容;
分类:
100-599
100: 通知浏览器返回的资源并不是一个独立的资源文件,需要浏览器接收到响应包之后继续发送请求获取其他资源;
200: 通知浏览器返回的资源是一个完整的独立资源,浏览器接到通知之后不需要继续获取其他资源;
302: 重定向,通知浏览器返回的不是一个资源文件,而是一个一个资源文件路径,需要浏览器格局这个地址发起请求来获取资源文件;重定向是可以重定向到本地服务器之外的服务器的;
404: 通知浏览器,浏览器地址栏的地址请求没有找到对应的资源文件,http服务器无法提供服务!
405: 一般是通知浏览器找到了对象的资源文件(Servlet) 但是请求方式错误;
500: 通知浏览器请求到了对象的资源文件,也程序也执行了,但是执行过程中出现了异常/错误,导致http服务器处理失败;
4) 问题4:
Servlet调用方案
1 重定向
2 请求转发
解析:
重定向:
原理: 用户通过第一次请求,去请求第一个资源(Servlet)当第一个Servlet执行完毕后,将要请求的第二个servlet的地址写入到响应头的location属性中,导致tomcat将302状态码写入到状态行中,浏览器读取到响应包的状态行的状态码就会发起第二次请求,去请求location中的地址;
实现: Servlet想要实现方式: response.sendRedirect(“请求地址”);
特征: 请求地址 重定向因为是浏览器再次发出的请求,所以请求地址可以跨服务器;
请求次数: 浏览器请求次数最少是两次;
请求方式: 因为重定向导致地址栏请求路径发生改变,所以请求方式一定是GET请求;
缺点: 缺点就是因为浏览器要发送一次又一次又一次的请求 ,所以会造成用户的体验效果不好,因为每一次请求都需要请求和响应时间等待;
请求转发
原理: 当浏览器发送一次请求之后,会在Servlet内部代替浏览器去向tomcat发送请求,申请调用第二个Servlet
实现命令:
RequestDispatcher report = request.getRequestDispatcher("/资源路径");//获取调度器
report.forward(request,response);//请求转发
优点: 优点是;浏览器只需要手动发送一次请求即可,请求调用在服务器内部进行,不需要再一次使用浏览器发起请求,节省了请求和响应时间;
特征:
只需要请求一次;
request.getRequestDispatcher("/资源路径");获取调度器时资源路径不能写域名网站,只能写当前服务器下的资源路径;
因为浏览器之请求一次,之后都是在服务器内部进行的转发请求,所以请求方式由第一次浏览器的请求方式有关,浏览器请求方式为GET转发请求就是GET,浏览器第一次请求是POST转发请求时就是POST请求;
5) 数据共享实现方案:
数据共享: 第一个Servlet执行完毕之后将产生的数据交给第二Servlet来使用;
实现方案:
1 ServletContext接口;
2 Cookie;
3 HttpSession接口;
4 HttpServletRequest接口;
ServletContext:
介绍:
1 来自于Servlet规范接口中的一个接口,在Tomcat中负责提供这个接口的实现类;
2 如果两个Servlet来自于同一个网站,彼此之间通过网站的ServletContext实例实现数据共享;
3 我们习惯称之为全局作用域;
工作原理:
每一个网站都存在一个全局作用域对象,这个全局作用对象相当于一个Map集合,一个Servlet将数据存储到全局作用域中,之后,在这个网站中的其他Servlet都可以从这个全局作用域中获取数据;值得注意的是ServletContext作用域的内存空间是非常珍贵的,所以一般只存储非常重要的数据;
生命周期:
全局作用域其实是贯穿网站整个运行期间的;
在http服务器启动时会自动在***内存***中创建一个全局作用域;
在http服务器运行期间只有这样一个全局作用域;
在运行期间这个全局作用域一直会处于活跃状态;
当http服务器停止运行时(服务器关闭) http服务器将负责将这个全局作用域销毁;
实现命令:
//实现命令
// 获取全局作用域并存储数据
ServletContext context = request.getServletContext();
context.setAttribute("key1","测试数据一");
// 获取获取数据
ServletContext context = request.getServletContext();
String str = (String)context.getAttribute("key1");
Cookie:
介绍:
Cookie来自于Servlet规范中的一个工具类;
Cookie存放当前用户的私人数据,在数据共享中提高服务质量;
如果两个Servlet来自于同一个网站,并且为同一个浏览器(用户)提供服务,此时需要使用Cookie来进行数据共享;
在实际生活中相当于我们的会员卡一样,第一次来了要求你办卡,下一次来了你就得出示你的会员卡;
原理:
当浏览器第一次向服务器请求访问OneServlet,然后OneServlet在执行期间创建一个Cookie存储当前用户的相关数据,当OneServlet执行完毕之后,会将Cookiie写入到响应头,然后返回个浏览器;
浏览器收到响应包之后,将cookie在浏览器缓存一段时间,用户再次通过同一个浏览器向服务器发送请求时,浏览器需要无条件的将存储在浏览器中的cookie写入到请求头中,发送给服务器,此时TwoServlet就可以获取cookie中的数据得到OneServlet共享的数据;
//实现命令:
Cookie cookie1 = new Cookie("key1","测试数据2");//cookie只能存放一个键值对,并且key不能使用汉字;key 和 value必须是字符串类型
response.addCookie(cookie1);
response.addCookie(cookie2);//Cookie可以设置多个返回给客户端;
// 再次请求时获取Cookie
Cookie cookieArray[] = request.getCookies();
//接下来就是循环获取数据
cookie.getValue();
cookie.getName();
5) Cookie的生命周期?
解析:
1 Cookie是保存在浏览器中的,所以默认请情况下只要浏览器关闭Cookie就会被销毁;
2 我们可以设置Cookie的存活时间,我们设置Cookie’的存活时间其实就是将Cookie保存在硬盘上一段时间,
这段时间即使你关闭浏览器再打开Cookie也还是存在的,时间一过去,Cookie就会存硬盘上消失,这样重新打开浏览器Cookie就会消失;
//设置Cookie的存活时间:
Cookie cookie = new Cookie("key","value");
cookie.setMaxAge(60);//单位是秒s
response.addCookie(cookie);
6) HttpSession接口:(Session对象事项数据的共享)
介绍:
HttpSession是Servlet规范中的一个接口,存在于Tomcat的servlet-api.jar;他的实现类由Http服务器提供;
如果两个Servlet存在于同一个网站,并且为同一个浏览器/用户提供服务,此时可以借助Session来实现数据共享;
开发中我们习惯将Session称之为会话称之为会话作用域对象;
Cookie与Session的区别:
1 相同点是他们都是用于Servlet之间用于通信的;
2 存储位置不同: Cookie存储在当前用户的计算机中(不仅包括浏览器还包括硬盘,因为设置了Cookie的存活时间之后,Cookie就会被保存在硬盘);HttpSession存储在服务器的计算机内存中;
3 存储数据类型不同: Cookie只能存储String类型的键值对,并且key不能使用中文;Session可以存储任意类型的数据,Object;
//OneServlet中Session实现数据共享
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//获取Session
HttpSession session = request.getSession();
session.setAttribute("key",共享的数据);
}
}
//TwoServlet中获取共享数据
TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//获取Session
HttpSession session = request.getSession();
//获取共享数据
Object data = session.getAttribute("key");
}
}
HttpSession和Cookie是如何关联起来的?
其实当我们浏览器第一次访问服务器的时候,会将我们的一些信息记录到Session中,并且返回个客户端一个标识JSSSIONID,这个标识就对应着我们的这个Session,等浏览器下次过来的时候,需要提供这个Cookie,服务器根据标识查找我们的Session获取共享数据,这样一来Cookie就和Session关联起来了;
getSession和getSession(false)的区别?
第一个获取Session的时候,如果已经存在了这个Session,就会直接将这个Session返回;如果当前用户尚未拥有自己的Session就会让Tomcat服务器为当前用户创建一个Session
后者,也是如果存在就直接返回,但是如果当前用户没用自己的Session,并不会调用Tomcat去创建Session;
Session的生命周期,其实就是什么时候会被销毁?
解析:
我们知道Session在创建时会创建一个Cookie作为这个Session唯一标识,这个Cookie会保存在浏览器中,我们知道,一旦浏览器关闭Cookie就会消亡,
但是Tomcat服务器并不知道浏览器什么时候断开,所以用户对应的Session不会消失;
Tomcat为了解决浏览器断开Session没有销毁的状况导致占用内存,
所以Tomcat会将这个Session保存30分钟,30分钟后就会将此Session自动销毁;
但是 Tomcat将Session保存30分钟对于用运行内存有限的服务器,这已经是很"奢侈"的一种操作了,一般我们不会让Session保存这么长时间,所以我们可以手动设置Session的空闲存活时间
<!--手动设置Session的空闲存活时间(单位分钟): 在web.xml中设置如下-->
<session-config>
<session-timeout>5</session-timeout>
</session-config>
7) HttpServletRequest接口实现数据共享?
解析
如果同一个网站中,两个Servlet通过请求转发进行调用,那么这两个Servlet其实是共享的一个请求协议包,而一个请求协议包只能对应一个请求对象Request,所以两个Servlet之间是使用的同一个HttpServletRequest的实现类的,所以可以利用这个Request请求对象事项数据的共享;
开发中我们称之为请求作用域;
实现命令如下:
//实现原理:第一个Servlet中设置共享数据
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
request.setAttribute("key",共享数据);
request.getRequestDispatcher("/第二个Servlet的请求路径").forward(request,response);
}
}
//实现原理:第二个Servlet中获取共享数据
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
Object data = request.getAttribute("key");
}
}
8) 监听器接口
介绍:
1 一组来自于Servlet规范下的接口,共有8个接口,在Tomcat中存在于servlet-api.jar包中;
2 监听器接口并有对应的实现类,它需要我们开发者自己进行实现;
3 监听器接口用于监控作用域对象的生命周期变化时刻 以及 作用域对象共享数据的变化时刻
Servlet作用域对象:
概念:在Servlet的开发规范中,两个Servlet用于共享数据的对象称之为作用域对象:
Servlet规范下的作用域:
ServletContext: 全局作用域对象;
HttpSession : 会话作用域对象;
HttpServletRequest : 请求作用域对象;
监听器接口的实现的三个步骤;
1 根据监听的实际情况,选择对应的接听器接口进行实现;
2 重写监听器接口声明[监听事件处理方法]
3 在web.xml中将此实现类注册到http服务器;
ServletContextListener : 监听器接口
此监听器接口,用于监听全局作用域对象的初始化和被销毁的时刻
对应的方法:
public class ContectListener implements ServletContextListener {
//监听ServletContext全局作用域的初始化
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
System.out.println(servletContext+"全局作用与对象初始化完成!!!");
}
// 监听全局作用域的销毁
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
System.out.println(servletContext+"全局作用域对象被销毁!!!");
}
}
<!--注册监听器实例化对象-->
<listener>
<listener-class>show.mrkay.listener.ContectListener</listener-class>
</listener>
ServletContextAttributeListener : 监听器接口
作用: 作用是用于监听全局作用域对象中的共享数据的变化情况
接口中方法如下:
//监听器
public class ContextAttrbuteListener implements ServletContextAttributeListener {
//监听全局作用域中添加共享数据
@Override
public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
System.out.println("新增了共享数据!!!");
}
//监听全局作用域中删除共享数据
@Override
public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
System.out.println("删除了共享数据!!!");
}
//监听全局作用域中替换共享数据
@Override
public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
System.out.println("替换了共享数据!!!");
}
}
//web.xml
<!--注册全局属性监听器实例化对象-->
<listener>
<listener-class>show.mrkay.listener.ContextAttrbuteListener</listener-class>
</listener>
//Servlet中
public class OneServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = request.getServletContext();
servletContext.setAttribute("key1", "testdata01");
servletContext.setAttribute("key1", "updatedata01");
servletContext.removeAttribute("key1");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
ServletContextListener监听器接口,知识拓展:
ServletContextListener其实有一个作用,我们可以通过此监听器接口实现: 数据库连接池;
也就是我们可以创建多个Connection连接对象;
实现方式就是:
在ServletContextListener接口实现类中监听初始化全局作用域的方法中我们一次性创建很多歌Connection对象,
将这些对象存入Map中并放到ServletContext全局作用域中在使用的时候直接从ServletContext中进行取出来使用即可;
9) Filter过滤器接口
介绍:
来自于Servlet规范中的接口;在Tomcat的servlet-api.jar包中;
Filter接口也不提供实现类,需要开发者根据需要自己进行实现,然后重写其中方法即可;
Filter接口在Http服务器在调用资源文件之前,对http服务器的请求进行拦截;
作用:
拦截Http服务器请求,判断当前请求是否合法;
拦截Http服务器请求,判断对当前请求进行增强;
Filter接口实现步骤:
1 创建Filter实现类;
2 重写其中方法;(doFilter方法)
3 在web.xml中注册该过滤器实现类;
// 过滤器实现类
public class OneFilter implements Filter {
//过滤器初始化方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化");
}
//doFilter方法:用于对具体的请求进行过滤或者增强
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String age = servletRequest.getParameter("age");
int intAge = Integer.parseInt(age);
if (intAge > 18) {
filterChain.doFilter(servletRequest,servletResponse);
} else {
servletResponse.setContentType("text/html;charset=UTF-8");
servletResponse.getWriter().print("<h1>不好意思您没有18岁!</h1>");
}
}
//销毁方法
@Override
public void destroy() {
System.out.println("销毁结束!!!");
}
}
<filter>
<filter-name>oneFilter</filter-name>
<filter-class>show.mrkay.filter.OneFilter</filter-class>
</filter>
<!--请求mm.jpg时进行拦截-->
<filter-mapping>
<filter-name>oneFilter</filter-name>
<url-pattern>/mm.jpg</url-pattern>
</filter-mapping>
注意事项:
此外我们还可以使用过滤器进行增强操作
比如我们可以
在doFilter()方法中使用request.setCharsetEncoding(“UTF-8”);来完成字符集的统一;
然后在web.xml中配置请求任何路径都进行拦截即可
<filter-mapping>
<filter-name>oneFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Filter拦截器在web.xml中配置拦截地址格式如下:
<!--拦截所有路径-->
<filter-mapping>
<filter-name>oneFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--拦截某一个目录下的资源-->
<filter-mapping>
<filter-name>oneFilter</filter-name>
<url-pattern>/img/*</url-pattern>
</filter-mapping>
<!--拦截某一类型的文件-->
<filter-mapping>
<filter-name>oneFilter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<!--拦截具体某一个文件-->
<filter-mapping>
<filter-name>oneFilter</filter-name>
<url-pattern>/img/mm.png</url-pattern>
</filter-mapping>
Filter过滤器实际应用:
用于防止用户恶意登录
实现方式:
1 在用户登录时,如果用户合法则设置一个Session给用户;request.getSession();
2 创建一个Filter过滤器实现类,配置拦截所有请求;
3 在doFilter()方法中创建使用request.getSession(false);获取session;
4 如果获取到,就直接放行,获取不到直接跳转到登录页面即可;