前言
感谢
Servlet简介
Servlet,其实就是一个接口,这个接口定义了Java类被浏览器访问到服务器的规则
如果我们定义一个类去实现Servlet接口,那么我们实现的这个类也可以被浏览器识别
起步
HelloServlet
我们用一个新的项目来说明如何编写一个Servlet类
1、新建一个项目,这个项目需要选择项目的原型,首先肯定是maven项目,然后我们需要选择项目的骨架,next
2、随便起个什么名字,然后一直下一步,直到项目创建完毕
3、我们看到了pom.xml文件,需要导入servlet的依赖,依赖如下
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
4、补全目录
或许javaweb项目创建出来之后是不完整的,所以我们需要手动补全目录结构,结构如下:
5、web.xml
现在的web.xml貌似有一些改版,不过影响不大,现在的改版之后貌似是这样的:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
</web-app>
不过为了我们的学习使用,我们还是使用老版本的
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
</web-app>
不管使用哪个版本,我们在一开始学习的时候,除了上述的几个标签之外,其余的全部都删除,保留一个原始纯净的样子,其他的标签后面会慢慢讲到
6、HelloServlet
我们在之前讲过,只要实现Servlet就可以被浏览器访问,现在我们新建一个类实现Servlet类,遵循传统,我们的名字起名为HelloServlet
package com.howling;
import javax.servlet.*;
import java.io.IOException;
public class HelloServlet implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
/**
* Servlet为我们提供服务的方法
*
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
service,就是为我们提供服务的方法
6、在web.xml中填充如下内容
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.howling.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
其中,servlet-class标签里的内容填充为你自己的全类名 注意,servlet-mapping可以定义多个,这说明我们可以通过多个路径来访问到资源
7、设置tomcat服务器
1、idea右上方有一个添加配置
2、点击加号,选择你的tomcat服务器
3、配置你的tomcat服务器的本地地址,url,http端口号
4、点击部署,添加你的项目到部署中
5、选择完成之后,会出现一个地址
6、然后我们在回到服务器选项中,发现url改动了
7、保存这个设置
8、启动
出现了这个页面,代表着我们的服务已经成功了
9、在tomcat启动后日志打印乱码
在你的 tomcat/conf/logging.properties 下面改动如下部分
1catalina.org.apache.juli.AsyncFileHandler.encoding = GBK
2localhost.org.apache.juli.AsyncFileHandler.encoding = GBK
3manager.org.apache.juli.AsyncFileHandler.encoding = GBK
4host-manager.org.apache.juli.AsyncFileHandler.encoding = GBK
java.util.logging.ConsoleHandler.encoding = GBK
10、修改Tomcat的配置内容
我们在访问tomcat的时候如果前方要加上一长串内容总是让我们很不爽,所以更改一下
![]()
改动完成之后在执行一次
11、访问Servlet
我们在xml的配置是:/hello,这个是访问的路径,前面要加上ip和端口号,我们已经在上一步就配置好了,所以完整的路径是:
127.0.0.1:8080/hello
,其中127.0.0.1
可以使用localhost
来代替,代表着本机,所以没有内容很正常,毕竟我们没有编写页面内容,但是我可以提前透露一个内容,只需要修改service方法即可
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.getWriter().println("Servlet");
servletResponse.getWriter().close();
}
然后我们重启服务器,查看这个网址,发现
Servlet生命周期
Servlet的生命周期
1、init:创建,执行了init方法,在生命周期中只执行一次
Servlet创建的时间: 1、第一次被访问的时候创建Servlet,也是默认情况 2、可以在web.xml下面配置Servlet的创建时间:xml中使用
<load-on-startup>
- 第一次被访问时创建,值为-1
- 服务器启动被创建:只要是大于等于0的数即可
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.howling.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Servlet创建只执行一次说明了 1、一个Servlet在内存中只存在一个 2、Servlet是单例的 3、多个用户同时访问的时候,可能存在线程问题 4、为了防止线程不安全,尽量不要在Servlet中定义成员变量,即使定义,也不要修改这个变量的值,否则会出现线程问题 5、我们要尽量使用局部变量来代替成员变量
2、service方法:执行多次,每次访问service时都会调用一次
3、destory方法:只执行一次
1、在被销毁之前执行一次,服务器关闭的时候会被销毁 2、只有服务器正常关闭的时候才会执行这个方法,非正常关闭不会执行 3、在Servlet被销毁之前执行,一般用于释放资源
证明Servlet生命周期
为了证明我说的是对的,我们分别在这几个方法中打印几句话
public class HelloServlet implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init...");
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service...");
}
public void destroy() {
System.out.println("destory...");
}
}
然后我们重新启动服务器,多次访问servlet,最后关闭服务器,看它打印的结果
通过测试我们可以发现 1、在调用servlet的一瞬间,首先打印了init,然后打印了service 2、多次访问servlet,只打印了service 3、在关闭服务器的时候,首先打印了一串日志,然后打印了destory
Servlet接口各个方法的含义
package com.howling;
import javax.servlet.*;
import java.io.IOException;
public class HelloServlet implements Servlet {
/**
* Servlet生命周期的初始化,只执行一次,默认在访问Servlet的时候执行
*
* @param servletConfig
* @throws ServletException
*/
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init...");
}
/**
* Servlet的配置对象
*
* @return
*/
public ServletConfig getServletConfig() {
return null;
}
/**
* Servlet提供服务的方法,访问Servlet的时候执行
*
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service...");
}
/**
* 获取Servlet的信息,版本,作者等等
*
* @return
*/
public String getServletInfo() {
return null;
}
/**
* Servlet销毁时的方法,只执行一次
*/
public void destroy() {
System.out.println("destory...");
}
}
Servlet3.0
为什么要单独拿出Servlet3.0呢,因为在Servlet3.0中,我们可以直接使用注解来代替xml
也就是说,我们不需要web.xml了,下面我来演示一下
1、web.xml的属性更改
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
</web-app>
主要是
metadata-complete
更改为false 当这个属性为true的时候,忽略所有注解 当这个属性为false的时候(或者删除这个属性),注解和xml同时生效,但是如果使用xml的方式访问的时候,注解会失效
2、删除web.xml中servlet和servlet-mapping的配置
3、修改Servlet
package com.howling;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/hello")
public class HelloServlet implements Servlet {
/**
* Servlet生命周期的初始化,只执行一次,默认在访问Servlet的时候执行
*
* @param servletConfig
* @throws ServletException
*/
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init...");
}
/**
* Servlet的配置对象
*
* @return
*/
public ServletConfig getServletConfig() {
return null;
}
/**
* Servlet提供服务的方法,访问Servlet的时候执行
*
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service...");
}
/**
* 获取Servlet的信息,版本,作者等等
*
* @return
*/
public String getServletInfo() {
return null;
}
/**
* Servlet销毁时的方法,只执行一次
*/
public void destroy() {
System.out.println("destory...");
}
}
或许你们注意到了,我们在类上加了一个注解:
@WebServlet
,这个注解可以代替我们之前的
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.howling.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
重启服务器,来看一下
4、注解的多个路径
注解可以定义多个路径,这多个路径都可以访问这一个Servlet,比如:
@WebServlet({"/hello","/demo1","/demo"})
路径的匹配规则 1、/xxx:最基础的访问路径 2、/xxx/xx:多层访问路径,在访问的时候要访问/xxx/xx 3、/xx/:在访问的时候只需要访问/xx/xxx,其中xxx可以随便填 4、.do:扩展名的匹配,这里不用加斜杠,只要访问xxx.do就可访问到Servlet,xxx可以随便填,do也可以换别的
5、对于Service3.0
我们在学习阶段,建议首先使用xml的方式来配置javaweb项目,以后在快速开发的时候不妨使用3.0的注解
Servlet的更多内容
Servlet原理
浏览器向web容器(服务器)发出http请求
1、web容器生成一个Servlet(只有首次请求会生成)
2、web容器生成两个对象
- 请求:处理客户端请求
- 响应:响应客户端请求
3、web容器激活servlet的service方法,传递请求和响应对象作为参数
4、service()获取请求对象的信息,处理请求信息
5、service()使用响应对象的方法将响应传递回web容器
6、web容器响应给客户端
7、重复3-5的过程
8、销毁
Servlet的体系结构
其实关于Servlet,并不是只有一个接口,它的结构是这样的:
从图中可以非常清楚的看到,Servlet作为一个接口,GenericServlet和HttpServlet都直接或者间接的实现了它
我们最常使用的就是HttpServlet,因为它对Servlet做了很多的封装和默认参数调整,我们只需要继承HttpServlet然后就可以非常轻松的编写代码了
HttpServlet
HttpServlet起步
1、继承HttpServlet
2、重写doGet和doPost方法
package com.howling;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
关于重写doGet和doPost的这个事情,不知道是否还记得我们曾经在学习前端的时候,在表单提交那一块内容我们曾经讲过,表单的提交有两种方式,一种叫get方式,一种叫post方式
那么这两种方式就对应着这两种方法
post提交会访问doPost方法,get提交会访问doGet方法
HttpServlet源码分析
虽然我们知道Servlet的体系结构,但其实GenericServlet没有什么好分析的,我们只需要看HttpServlet即可
图中的几个方法都是比较重要的,我们其实可以看到,除了doGet和doPost还有这么多的请求方式 但是在这里我们先不做探讨,以后早晚会用到的
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
//看看请求和响应是不是HTTP的请求和响应,如果是就直接转换,如果不是抛出异常,说明这个请求响应不是http协议的
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
this.service(request, response);
} else {
throw new ServletException("non-HTTP request or response");
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求方式
String method = req.getMethod();
long lastModified;
//假如为get请求,那么...
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
//假如为head请求,那么...
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
//假如为post请求,那么调用doPost
} else if (method.equals("POST")) {
this.doPost(req, resp);
//假如为put请求,那么调用doPut
} else if (method.equals("PUT")) {
this.doPut(req, resp);
//....
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
这段代码虽然看起来多,但是逻辑非常清晰,到了最后总是要调用我们这几个doxxx的方法的 而这几个doxxx的方法我们是要进行重写的
ServletContext
web容器在启动的时候,会给每一个web程序都创建一个ServletContext,它代表着当前的web应用,和servlet不同,ServletContext在每个web应用中都只有一个
ServletContext有几个作用
1、共享数据
2、获取初始化参数
3、请求转发
4、读取资源文件
共享数据
package com.howling;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/world")
public class WorldServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("username","howling");
}
}
package com.howling;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
ServletContext servletContext = this.getServletContext();
System.out.println(servletContext.getAttribute("username"));
}
}
获取初始化参数
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql:///student</param-value>
</context-param>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.howling.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
package com.howling;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
ServletContext servletContext = this.getServletContext();
String url = servletContext.getInitParameter("url");
System.out.println(url);
}
}
请求转发
package com.howling;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello");
ServletContext servletContext = this.getServletContext();
RequestDispatcher dispatcher = servletContext.getRequestDispatcher("/world");
dispatcher.forward(req, resp);
}
}
package com.howling;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/world")
public class WorldServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("World");
}
}
读取资源文件
首先要说明一下,在编译之前和编译之后的资源文件地址
这里打包后的classes我们俗称为classpath
我们在resources中添加一个properties文件,根据上面的图片,我们可以得到最终这个文件会在WEB-INF/classes中
package com.howling;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
InputStream stream = servletContext.getResourceAsStream("/WEB-INF/classes/hello.properties");
Properties properties = new Properties();
properties.load(stream);
resp.getWriter().print(properties.getProperty("username") + "--"+properties.getProperty("password"));
System.out.println();
stream.close();
}
}
HttpServletRequest
HttpServletRequest,代表的是客户端的请求,用户使用Http协议请求服务器,Http请求中的信息会被封装到HttpServletRequest中,通过这个方法获得客户端的所有信息
获取请求行
String getMethod()
:获取请求的方式String getContextPath()
:获取站点的根目录String getQueryString()
:获取get方式的请求参数String getRequestURI()
:获取请求的URIStringBuffer getRequestURL()
:获取URLURL:统一资源定位符 URI:统一资源标识符 URI比URL的范围更大
String getProtocol()
:获取协议和版本String getRemoteAddr()
:获取客户机的IP地址String getScheme()
:获取当前页面使用的协议,默认是http,SSL时返回httpsString getServerName()
:可以返回当前页面所在的服务器的名字String getServerPort()
:获取当前页面服务器所使用的端口号
在敲代码之前,首先我们要将tomcat的前缀名加上
package com.howling;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
//获取请求方式
writer.write("getMethod--" + req.getMethod() + "\n");
//获取站点的根目录
writer.write("getContextPath--" + req.getContextPath() + "\n");
//获取get方式的请求参数
writer.write("getQueryString--" + req.getQueryString() + "\n");
//获取请求的uri
writer.write("getRequestURI--" + req.getRequestURI() + "\n");
//获取请求的url
writer.write("getRequestURL--" + req.getRequestURL() + "\n");
//获取协议和版本
writer.write("getProtocol--" + req.getProtocol() + "\n");
//获取客户端的ip地址
writer.write("getRemoteAddr--" + req.getRemoteAddr() + "\n");
//获取协议
writer.write("getScheme--" + req.getScheme() + "\n");
//获取当前页面所在的服务器的名字
writer.write("getServerName--" + req.getServerName() + "\n");
//获取页面服务器所使用的端口号
writer.write("getServerPort--" + req.getServerPort() + "\n");
writer.close();
}
}
获取请求头
Enumeration<String> getHeaderNames()
:获取请求头的名称
package com.howling;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
writer.write(headerNames.nextElement()+"\n");
}
writer.close();
}
}
获取请求体
BufferedReader getReader()
:获取字符输入流,只能操作字符数据ServletInputStream getInputStream()
:获取字节输入流,可以操作所有类型的数据String getParameter(String name)
:根据参数名称获取参数值String[] getParameterValues()
:获取所有请求的参数名称Map<String,String[]> getParameterMap()
:获取所有参数的Map集合
中文乱码问题
- get方式:tomcat8之后已经解决了get乱码
- post方式:乱码的解决需要手动解决
在获取参数之前,设置request的编码
request.setCharacterEncoding(“utf-8”)
这个作用是设置客户端请求和数据库取值时的编码
请求转发
1、步骤
1、通过request对象获取请求转发对象:
RequestDispatcher getRequestDispatcher(String path)
2、使用RequestDispatcher对象来进行转发:forward(ServletRequest request,ServletResponse response)
2、特点
1、浏览器地址栏路径不发生变化 2、只能转发到当前服务器内部资源中,不能再访问其他服务器资源 3、转发是一次请求,虽然两个资源同时被访问,但其实是一次请求
package com.howling;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/world").forward(req,resp);
}
}
HttpServlerResponse
web服务器接受到客户端的请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表着响应的一个HttpServletResponse
向浏览器发送数据
ServletOutputStream getOutputStream()
PrintWriter getWriter()
向浏览器发送响应头
setContentLength(int val)
setContentLengthLong(long var)
setDateHeader(String var1, long var2)
setHeader(String var1, String var2)
setIntHeader(String var1, int var2)
中文乱码问题
response.setCharacterEncoding(“utf-8”)
这个作用是设置服务器响应给浏览器的编码
response.setContentType(“text/html;charset=utf-8”)
这个作用是设置服务器响应给浏览器的编码,并且浏览器也根据这个参数来进行解码
下载文件
1、获取下载文件的路径
2、获取下载的文件名字
3、设置浏览器的编码,否则可能会乱码
4、获取下载文件的输入流
5、file和io那一套东西
package com.howling;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String realPath = this.getServletContext().getRealPath("\\img\\1.png");
//D:\Environment\Tomcat\apache-tomcat-9.0.14\webapps\servlet\img\1.png
System.out.println(realPath);
String filename = realPath.substring(realPath.lastIndexOf("\\"));
//\1.png
System.out.println(filename);
//设置之后会自动读取文件类型
resp.setContentType("multipart/form-data");
//设置请求头,其中filename是文件的名字
resp.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filename, "UTF-8"));
FileInputStream inputStream = new FileInputStream(realPath);
int len = 0;
byte[] buffer = new byte[1024];
ServletOutputStream outputStream = resp.getOutputStream();
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
}
inputStream.close();
outputStream.close();
}
}
后端实现验证码
1、设置浏览器的刷新
2、通过BufferedImage创建图片
3、Graphics2D得到图片并且写入数据
4、response告诉浏览器编码方式
5、取消浏览器缓存
6、写入到浏览器
package com.howling;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//让浏览器三秒自动刷新一次
resp.setHeader("refresh", "3");
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_BGR);
Graphics2D graphics = (Graphics2D) image.getGraphics();
//背景颜色
graphics.setBackground(Color.white);
graphics.fillRect(0, 0, 80, 20);
//给图片写数据
graphics.setColor(Color.BLUE);
graphics.setFont(new Font(null, Font.BOLD, 20));
graphics.drawString(makeNum(), 0, 20);
//告诉浏览器,请求用图片打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires", -1);
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
ImageIO.write(image, "jpg", resp.getOutputStream());
}
private String makeNum() {
Random random = new Random();
String num = random.nextInt(99999) + "";
return num;
}
}
重定向
sendRedirect()
重定向和转发 相同点:页面都会实现跳转 不同点 1、请求转发时url不会变化,而重定向url会变化 2、转发是服务器的行为,重定向是客户端的行为 3、请求转发是一次请求,重定向是多次请求 4、请求转发共享数据,重定向不共享数据 5、请求转发只能定位到本服务器资源,重定向可以定位到任意的url
package com.howling;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("https://www.baidu.com");
}
}
Servlet线程安全问题
首先我要说,Servlet不是线程安全的。
1、首先,Servlet是单例的
2、然后,仅有HttpServletRequest是针对不同请求(线程)而有不同实例的,其他都是共享的。
3、根据以上两点情况,Servlet不是线程安全的
当然了,除了HttpServletRequest是线程安全的,HttpContext和HttpSession都不是线程安全的