JavaWeb之Servlet技术(二),java面试题最新

死亡:当服务停止时,或者服务器宕机时,对象消亡。

通过分析Servlet的生命周期我们发现,它的实例化和初始化只会在请求第一次到达Servlet时执行,而销毁只会在Tomcat服务器停止时执行,由此我们得出一个结论,Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。

1.4.1、启动时创建Servlet

我们前面讲解了Servlet的生命周期,Servlet的创建默认情况下是请求第一次到达Servlet时创建的。但是我们都知道,Servlet是单例的,也就是说在应用中只有唯一的一个实例,所以在Tomcat启动加载应用的时候就创建也是一个很好的选择。那么两者有什么区别呢?

  • 第一种:应用加载时创建Servlet,它的优势是在服务器启动时,就把需要的对象都创建完成了,从而在使用的时候减少了创建对象的时间,提高了首次执行的效率。它的弊端也同样明显,因为在应用加载时就创建了Servlet对象,因此,导致内存中充斥着大量用不上的Servlet对象,造成了内存的浪费。

  • 第二种:请求第一次访问是创建Servlet,它的优势就是减少了对服务器内存的浪费,因为那些一直没有被访问过的Servlet对象都没有创建,因此也提高了服务器的启动时间。而它的弊端就是,如果有一些要在应用加载时就做的初始化操作,它都没法完成,从而要考虑其他技术实现。

通过上面的描述,相信我们都能分析得出何时采用第一种方式,何时采用第二种方式。就是当需要在应用加载就要完成一些工作时,就需要选择第一种方式。当有很多Servlet的使用时机并不确定是,就选择第二种方式。

在web.xml中是支持对Servlet的创建时机进行配置的,配置的方式如下:我们就以ServletDemo3为例。

  • 配置标签:<load-on-startup> 1 </load-on-startup>

  • 配置项的取值只能是正整数(包括0),数值越小,表明创建的优先级越高

servletDemo3

com.itheima.web.servlet.ServletDemo3

1

servletDemo3

/servletDemo3

1.4.2、默认Servlet

默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中

default

org.apache.catalina.servlets.DefaultServlet

debug

0

listings

false

1

它的映射路径是<url-pattern>/<url-pattern>,我们在发送请求时,首先会在我们应用中的web.xml中查找映射配置,找到就执行,这块没有问题。但是当找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。所以,一切都是Servlet。

1.5、Servlet线程安全


  • 由于Servlet运用了单例模式,即整个应用中只有一个实例对象,所以我们需要分析这个唯一的实例中的类成员是否线程安全。

  • 在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据。其实每一个浏览器端发送请求,就代表是一个线程,那么多个浏览器就是多个线程,所以说明了多个线程会共享Servlet类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,我们可以认为Servlet它不是线程安全的。

  • 分析产生这个问题的根本原因,其实就是因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。

  • 解决这个问题也非常简单,就是在Servlet中定义类成员要慎重。如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线程安全问题了,把它定义到doGet或者doPost方法里面去就可以了。

1.6、Servlet原理


Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:

在这里插入图片描述

2.ServletContext

==================================================================================

2.1、基本介绍


  • ServletContext对象,它是应用上下文对象

  • web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用

  • 它可以实现让应用中所有Servlet间的数据共享。

2.2、生命周期


出生——活着——死亡

出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)

活着:只要应用一直提供服务,该对象就一直存在。

死亡:应用被卸载(或者服务器挂了),该对象消亡。

2.3、域对象


在Servlet规范中,一共有4个域对象。ServletContext就是其中一个。它也是我们接触的第一个域对象。它是web应用中最大的作用域,叫application域。每个应用只有一个application域。它可以实现整个应用间的数据共享功能。

2.4、共享数据


  • 我们在这个Servlet中保存的数据,可以在另外一个servlet中拿到

  • 但是都是通过同一个ServletContext对象拿到的

  • context.setAttribute(String name,Object value)

  • context.getAttribute(String name)

HelloServlet.java

public class HelloServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

ServletContext context = this.getServletContext();

//数据

String username = “秦疆”;

//将一个数据保存在了ServletContext中,名字为:username 。值 username

context.setAttribute(“username”,username);

}

}

  • 编写mapper映射

hello

com.kuang.servlet.HelloServlet

hello

/hello

GetServlet.java

public class GetServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

ServletContext context = this.getServletContext();

String username = (String) context.getAttribute(“username”);

resp.setContentType(“text/html”);

resp.setCharacterEncoding(“utf-8”);

// 将获取到的数据显示在网页中

resp.getWriter().print(“名字”+username);

}

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

doGet(req, resp);

}

}

  • 编写mapper映射

getc

com.kuang.servlet.GetServlet

getc

/getc

测试访问结果;

  • 先输入 : http://localhost:8080/s2/hello 访问/hello会存放数据

  • 再输入:http://localhost:8080/s2/getc 访问/getc会取出数据

在这里插入图片描述

在这里插入图片描述

2.5、获取初始化参数


  • 获取初始化参数:context.getInitParameter("参数名")

ServletContext既然被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配置。它的配置被称为应用的初始化参数配置。

我们可以在web.xml中配置一些web应用初始化参数,在ServletContext对象中可以拿到它

配置的方式,需要在<web-app>标签中使用<context-param>来配置初始化参数。具体代码如下:

GetInitParameter.java

/**

  • 获取初始化参数

*/

public class GetInitParameter extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

ServletContext context = this.getServletContext();

String url = context.getInitParameter(“url”);

// 将获取到的url打印到网页上

resp.getWriter().print(url);

}

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

super.doPost(req, resp);

}

}

在web.xml中配置初始化参数和注册servlet映射为 /gp

url

jdbc:mysql://localhost:3306/mybatis

globalEncoding

UTF-8

gp

com.kuang.servlet.GetInitParameter

gp

/gp

  • 我们访问测试

在这里插入图片描述

2.6、请求转发


  • 请求转发:context.getRequestDispatcher("转发的请求路径").forward(req,resp)

GetRequestDispatcher.java

/**

  • 请求转发

*/

public class GetRequestDispatcher extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

System.out.println(“进入了GetRequestDispatcher”);

ServletContext context = this.getServletContext();

//RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); 转发的请求路径

//requestDispatcher.forward(req,resp); 调用forward实现请求转发;

context.getRequestDispatcher("/gp").forward(req,resp);

}

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

super.doPost(req, resp);

}

}

在web.xml中配置servlet映射为 /gr

gr

com.kuang.servlet.GetRequestDispatcher

gr

/gr

  • 测试访问

在这里插入图片描述

原因:我们请求/gr,但是/gr通过getRequestDispatcher转发到另外一个页面,但是我们访问的路径不变,依旧是localhost:8080/s2/gr

2.6.1、请求转发的特点

请求转发的特点:

  1. 浏览器地址栏路径不发生变化

  2. 只能转发到当前服务器内部资源中

  3. 转发是一次请求

2.7、读取资源文件


  • 在resources目录下新建db.properties

db.properties

username=root12312

password=zxczxczxc

PropertiesServlet.java

/**

  • 读取资源文件properties

*/

public class PropertiesServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

ServletContext context = this.getServletContext();

// 思路:需要一个文件流

// 这里的路径:

InputStream is = context.getResourceAsStream("/WEB-INF/classes/db.properties");

Properties prop = new Properties();

prop.load(is);

String username = prop.getProperty(“username”);

String password = prop.getProperty(“password”);

resp.getWriter().print(username +":"+password );

}

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

super.doPost(req, resp);

}

}

  • 这里的路径示意图

  • 因为java目录和resources目录均被打包至classes目录

在这里插入图片描述

  • 去web.xml中配置servlet映射

pr

com.kuang.servlet.PropertiesServlet

pr

/pr

  • 测试启动

在这里插入图片描述

2.8、ServletContext常用方法


public class ServletContextDemo extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//获取ServletContext对象

ServletContext context = this.getServletContext();

//获取全局配置的globalEncoding

String value = context.getInitParameter(“globalEncoding”);

System.out.println(value);

//获取应用的访问虚拟目录

String contextPath = context.getContextPath();

System.out.println(contextPath);

//根据虚拟目录获取应用部署的磁盘绝对路径

//获取b.txt文件的绝对路径

String b = context.getRealPath("/b.txt");

System.out.println(b);

//获取c.txt文件的绝对路径

String c = context.getRealPath("/WEB-INF/c.txt");

System.out.println©;

//获取a.txt文件的绝对路径

String a = context.getRealPath("/WEB-INF/classes/a.txt");

System.out.println(a);

//向域对象中存储数据

context.setAttribute(“username”,“zhangsan”);

//移除域对象中username的数据

context.removeAttribute(“username”);

}

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

doGet(req,resp);

}

}

3、HttpServletResponse

=======================================================================================

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求 HttpServletRequest对象,代表响应的一个HttpServletResponse对象

  • 如果要获取客户端请求过来的参数:找HttpServletRequest

  • 如果要给客户端响应一些信息:找HttpServletResponse

HttpServletrequest对象和HttpServleresponse对象的原理

  1. request和response对象是由服务器创建的。我们来使用它们

  2. request对象是来获取请求消息,response对象是来设置响应消息

3.1、关于响应


响应,它表示了服务器端收到请求,同时也已经处理完成,把处理的结果告知用户。简单来说,指的就是服务器把请求的处理结果告知客户端。在B/S架构中,响应就是把结果带回浏览器。

响应对象,顾名思义就是用于在JavaWeb工程中实现上述功能的对象。

3.2、常用方法介绍


在HttpServletResponse接口中提供了很多方法,接下来我们通过API文档,来了解一下这些方法。

在这里插入图片描述

3.3、常用状态码


| 状态码 | 说明 |

| :-: | :-: |

| 200 | 执行成功 |

| 302 | 它和307一样,都是用于重定向的状态码。只是307目前已不再使用 |

| 304 | 请求资源未改变,使用缓存。 |

| 400 | 请求错误。最常见的就是请求参数有问题 |

| 404 | 请求资源未找到 |

| 405 | 请求方式不被支持 |

| 500 | 服务器运行内部错误 |

3.4、状态码首位含义


| 状态码 | 说明 |

| :-: | :-: |

| 1xx | 消息 |

| 2xx

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

| 成功 |

| 3xx | 重定向 |

| 4xx | 客户端错误 |

| 5xx | 服务器错误 |

3.5、中文乱码问题


  • resp.setContentType("text/html;charset=UTF-8");

​ 在响应中,如果我们响应的内容中含有中文,则有可能出现乱码,这是因为服务器响应的数据也会经过网络传输,服务器有一种编码方式,在客户端也存在一种编码方式。当两端使用的编码方式不同时则会出现乱码。

​ 要解决该种乱码只能在服务器端告知服务器使用一种能够支持中文的编码格式,比如我们通常使用的"UTF-8"

// 指定服务器编码

resp.setCharacterEncoding(“UTF-8”);

// 指定客户端编码

resp.setHeader(“Content-type”,“text/html;charset=UTF-8”);

  • 以上两端编码的指定也可以使用一句代替,同时指定服务器和客户端(推荐)

resp.setContentType(“text/html;charset=UTF-8”);

示例:

public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

resp.setContentType(“text/html;charset=UTF-8”);

// 得到字符输出流

PrintWriter out = resp.getWriter();

out.write(“

你好

”);

}

public void doPost(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {

doGet(request, response);

}

访问测试:

在这里插入图片描述

3.6、响应数据


接收到客户端的请求后,可以通过 HttpServletResponse 对象直接进行响应,响应时需要获取输出流,服务器输出字符数据到浏览器有两种方法:

步骤:

  1. 获取字符输出流

  2. 输出数据

两种形式

  • resp.getWriter():获取字符流(只能响应回字符)

  • resp.getOutputStream():获取字节流(能响应一切数据)

注意:两者不能同时使用

3.6.1、字符输出流

public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

// 字符输出流(输出字符串)

PrintWriter writer = resp.getWriter();

writer.write(“hello”);

writer.write(“

Hello

”);

}

在这里插入图片描述

3.6.2、字节输出流

public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

// 字节输出流(输出一切数据)

ServletOutputStream out = resp.getOutputStream();

out.write(“hello”.getBytes());

out.write(“

Hello

”.getBytes());

}

在这里插入图片描述


为什么不能带标签式的输出呢?是因为我们没有指定编码,要使服务器输出字符数据到浏览器,必须指定编码,否则就按字符串格式显示在浏览器上

public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//中文乱码问题

resp.setContentType(“text/html;charset=UTF-8”);

//字节输出流(输出一切数据)

PrintWriter writer = resp.getWriter();

writer.write(“hello”);

writer.write(“

hello

”);

}

我们加上指定编码,再次访问此时:

在这里插入图片描述

3.7、重定向


  • 重定向resp.sendRedirect("请求路径")

  • 请求转发:A想要拿到C中的资源,但是A没法与C连接,A只能与B连接,于是A对B说,B你去C里面给我拿,B就与C建立连接,C把资源给B,B再把资源给A

  • 重定向:A连接B说想要拿到C中的资源,B对A说,你去连接C然后拿资源

在这里插入图片描述

一个web资源B收到客户端A的请求后,它B会通知A客户端去访问另一个web资源C,这个过程叫重定向

常见场景:

  • 用户登录

void sendRedirect(String var1);

RedirectServlet.java

/**

  • 实现重定向

*/

public class RedirectServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

/* 重定向的原理

1.设置响应状态码

resp.setStatus(302);

2.设置响应消息头Location

resp.setHeader(“Location”,"/r/img");

*/

resp.sendRedirect("/r/img");

}

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

super.doPost(req, resp);

}

}

  • 在web.xml中映射

redirect

com.kuang.servlet.RedirectServlet

redirect

/red

  • 测试启动

在这里插入图片描述

3.7.1、重定向的原理

//1.设置响应状态码

response.setStatus(302);

//2.定向到哪里去: 其实就是设置响应消息头,Location

response.setHeader(“Location”, “/r/img”);

//使用重定向方法

response.sendRedirect("/r/img");//此行做了什么事,请看上面

3.8、设置响应消息头


设置响应消息头控制缓存:resp.setDateHeader()

使用缓存的一般都是静态资源

  • 动态资源一般不能缓存。

  • 我们现在目前只掌握了Servlet,所以用Servlet做演示

public class ResponseDemo4 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String str = “设置缓存时间”;

/*

  • 设置缓存时间,其实就是设置响应消息头:Expires 但是值是一个毫秒数。

  • 使用的是

  • response.setDateHeader();

  • 缓存1小时,是在当前时间的毫秒数上加上1小时之后的毫秒值

*/

response.setDateHeader(“Expires”,System.currentTimeMillis()+16060*1000);

response.setContentType(“text/html;charset=UTF-8”);

response.getOutputStream().write(str.getBytes());

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

3.9、设置响应消息头定时刷新


设置响应消息头:通过定时刷新演示添加消息头

public class ResponseDemo5 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String str = “用户名和密码不匹配,2秒后转向登录页面…”;

response.setContentType(“text/html;charset=UTF-8”);

PrintWriter out = response.getWriter();

out.write(str);

//定时刷新,其实就是设置一个响应消息头

response.setHeader(“Refresh”, “2;URL=/login.html”);//Refresh设置的时间单位是秒,如果刷新到其他地址,需要在时间后面拼接上地址

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

在这里插入图片描述

3.8、文件下载


首先,在工程的webapp目录下新建一个目录uploads,并且拷贝一张图片(1.jpg)到目录中

文件下载的Servlet代码如下:

public class ResponseDemo8 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

/*

  • 文件下载的思路:

  •   1.获取文件路径
    
  •   2.把文件读到字节输入流中
    
  •   3.告知浏览器,以下载的方式打开(告知浏览器下载文件的MIME类型)
    
  •   4.使用响应对象的字节输出流输出到浏览器上
    

*/

//1.获取文件路径(绝对路径)

ServletContext context = this.getServletContext();

String filePath = context.getRealPath("/uploads/1.jpg");//通过文件的虚拟路径,获取文件的绝对路径

//2.通过文件路径构建一个字节输入流

InputStream in = new FileInputStream(filePath);

//3.设置响应消息头

response.setHeader(“Content-Type”, “application/octet-stream”);//注意下载的时候,设置响应正文的MIME类型,用application/octet-stream

response.setHeader(“Content-Disposition”, “attachment;filename=1.jpg”);//告知浏览器以下载的方式打开

//4.使用响应对象的字节输出流输出

OutputStream out = response.getOutputStream();

int len = 0;

byte[] by = new byte[1024];

while((len = in.read(by)) != -1){

out.write(by, 0, len);

}

in.close();

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

3.9、生成验证码


响应-生成验证码

public class ResponseDemo3 extends HttpServlet {

/**

  • 输出图片

  • @param request

  • @param response

  • @throws ServletException

  • @throws IOException

*/

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

int width = 200;

int height = 35;

/**

  • 实现步骤:

  • 1.创建图像内存对象

  • 2.拿到画笔

  • 3.设置颜色,画矩形边框

  • 4.设置颜色,填充矩形

  • 5.设置颜色,画干扰线

  • 6.设置颜色,画验证码

  • 7.把内存图像输出到浏览器上

*/

//创建内存图像

BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//参数:宽度,高度 (指的都是像素),使用的格式(RGB)

Graphics g = image.getGraphics();//画笔就一根

//设置颜色

g.setColor(Color.BLUE);

//画边框

g.drawRect(0, 0, width, height);

//设置颜色

g.setColor(Color.GRAY);

//填充矩形

g.fillRect(1, 1, width-2, height-2);

//设置颜色

g.setColor(Color.WHITE);

//拿随机数对象

Random r = new Random();

//画干扰线 10条

for(int i=0;i<10;i++){

g.drawLine(r.nextInt(width), r.nextInt(height),r.nextInt(width), r.nextInt(height));

}

//设置颜色

g.setColor(Color.RED);

//改变字体大小

Font font = new Font(“宋体”, Font.BOLD,30);//参数:1字体名称。2.字体样式 3.字体大小

g.setFont(font);//设置字体

//画验证码 4个

int x = 35;//第一个数的横坐标是35像素

for(int i=0;i<4;i++){

//r.nextInt(10)+""这种写法效率是十分低的

g.drawString(String.valueOf(r.nextInt(10)), x, 25);

x+=35;

}

//输出到浏览器上

//参数: 1.内存对象。2.输出的图片格式。3.使用的输出流

ImageIO.write(image, “jpg”, response.getOutputStream());

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

3.10、面试题


| 请求转发(req.getRequestDispatcher().forward()) | 重定向(resp.sendRedirect()) |

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值