1 Tomcat服务器
Tomcat目录结构
- bin:该目录下存放的是二进制可执行文件,如果是安装版,那么这个目录下会有两个exe文件:tomcat10.exe、tomcat10w.exe,前者是在控制台下启动Tomcat,后者是弹出GUI窗口启动Tomcat;如果是解压版,那么会有startup.bat和shutdown.bat文件,startup.bat用来启动Tomcat,但需要先配置JAVA_HOME环境变量才能启动,shutdawn.bat用来停止Tomcat;
-
conf:这是一个非常非常重要的目录,这个目录下有四个最为重要的文件:
-
server.xml:配置整个服务器信息。例如修改端口号。默认HTTP请求的端口号是:8080
-
tomcat-users.xml:存储tomcat用户的文件,这里保存的是tomcat的用户名及密码,以及用户的角色信息。可以按着该文件中的注释信息添加tomcat用户,然后就可以在Tomcat主页中进入Tomcat Manager页面了;
<tomcat-users xmlns="http://tomcat.apache.org/xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd" version="1.0"> <role rolename="admin-gui"/> <role rolename="admin-script"/> <role rolename="manager-gui"/> <role rolename="manager-script"/> <role rolename="manager-jmx"/> <role rolename="manager-status"/> <user username="admin" password="admin" roles="admin-gui,admin-script,manager-gui,manager-script,manager-jmx,manager-status" /> </tomcat-users>
web.xml:部署描述符文件,这个文件中注册了很多MIME类型,即文档类型。这些MIME类型是客户端与服务器之间说明文档类型的,如用户请求一个html网页,那么服务器还会告诉客户端浏览器响应的文档是text/html类型的,这就是一个MIME类型。客户端浏览器通过这个MIME类型就知道如何处理它了。当然是在浏览器中显示这个html文件了。但如果服务器响应的是一个exe文件,那么浏览器就不可能显示它,而是应该弹出下载窗口才对。MIME就是用来说明文档的内容是什么类型的!
-
context.xml:对所有应用的统一配置,通常我们不会去配置它。
-
-
lib:Tomcat的类库,里面是一大堆jar文件。如果需要添加Tomcat依赖的jar文件,可以把它放到这个目录中,当然也可以把应用依赖的jar文件放到这个目录中,这个目录中的jar所有项目都可以共享之,但这样你的应用放到其他Tomcat下时就不能再共享这个目录下的jar包了,所以建议只把Tomcat需要的jar包放到这个目录下;
-
logs:这个目录中都是日志文件,记录了Tomcat启动和关闭的信息,如果启动Tomcat时有错误,那么异常也会记录在日志文件中。
-
temp:存放Tomcat的临时文件,这个目录下的东西可以在停止Tomcat后删除!
-
webapps:存放web项目的目录,其中每个文件夹都是一个项目;如果这个目录下已经存在了目录,那么都是tomcat自带的项目。其中ROOT是一个特殊的项目,在地址栏中访问:http://127.0.0.1:8080,没有给出项目目录时,对应的就是ROOT项目.http://localhost:8080/examples,进入示例项目。其中examples"就是项目名,即文件夹的名字。
-
work:运行时生成的文件,最终运行的文件都在这里。通过webapps中的项目生成的!可以把这个目录下的内容删除,再次运行时会生再次生成work目录。当客户端用户访问一个JSP文件时,Tomcat会通过JSP生成Java文件,然后再编译Java文件生成class文件,生成的java和class文件都会存放到这个目录下。
-
LICENSE:许可证。
-
NOTICE:说明文件。
WEB项目的标准结构
一个标准的可以用于发布的WEB项目标准结构如下:
-
app 本应用根目录
-
static 非必要目录,约定俗成的名字,一般在此处放静态资源 ( css js img)
- WEB-INF 必要目录,必须叫WEB-INF,受保护的资源目录,浏览器通过url不可以直接访问的目录
- classes 必要目录,src下源代码,配置文件,编译后会在该目录下,web项目中如果没有源码,则该目录不会出现
- lib 必要目录,项目依赖的jar编译后会出现在该目录下,web项目要是没有依赖任何jar,则该目录不会出现
- web.xml 必要文件,web项目的基本配置文件. 较新的版本中可以没有该文件,但是学习过程中还是需要该文件
- WEB-INF 必要目录,必须叫WEB-INF,受保护的资源目录,浏览器通过url不可以直接访问的目录
-
index.html 非必要文件,index.html/index.htm/index.jsp为默认的欢迎页
-
url的组成部分和项目中资源的对应关系:
2 Servlet
Servlet (server applet) 是运行在服务端(tomcat)的Java小程序,是sun公司提供一套定义动态资源规范; 从代码层面上来讲Servlet就是一个接口
- 用来接收、处理客户端请求、响应给浏览器的动态资源。在整个Web应用中,Servlet主要负责接收处理请求、协同调度功能以及响应数据。我们可以把Servlet称为Web应用中的控制器
- 不是所有的JAVA类都能用于处理客户端请求,能处理客户端请求并做出响应的一套技术标准就是Servlet
- Servlet是运行在服务端的,所以 Servlet必须在WEB项目中开发且在Tomcat这样的服务容器中运行
Servlet开发流程
校验注册时,用户名是否被占用. 通过客户端向一个Servlet发送请求,携带username,如果用户名是’atguigu’,则向客户端响应 NO,如果是其他,响应YES
步骤1 开发一个web类型的module
- 过程参照之前
步骤2 开发一个UserServlet
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求中的参数
String username = req.getParameter("username");
if("atguigu".equals(username)){
//通过响应对象响应信息
resp.getWriter().write("NO");
}else{
resp.getWriter().write("YES");
}
}
}
- 自定义一个类,要继承HttpServlet类
- 重写service方法,该方法主要就是用于处理用户请求的服务方法
- HttpServletRequest 代表请求对象,是有请求报文经过tomcat转换而来的,通过该对象可以获取请求中的信息
- HttpServletResponse 代表响应对象,该对象会被tomcat转换为响应的报文,通过该对象可以设置响应中的信息
- Servlet对象的生命周期(创建,初始化,处理服务,销毁)是由tomcat管理的,无需我们自己new
- HttpServletRequest HttpServletResponse 两个对象也是有tomcat负责转换,在调用service方法时传入给我们用的
步骤3 在web.xml为UseServlet配置请求的映射路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<servlet>
<!--给UserServlet起一个别名-->
<servlet-name>userServlet</servlet-name>
<servlet-class>com.atguigu.servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<!--关联别名和映射路径-->
<servlet-name>userServlet</servlet-name>
<!--可以为一个Servlet匹配多个不同的映射路径,但是不同的Servlet不能使用相同的url-pattern-->
<url-pattern>/userServlet</url-pattern>
<!-- <url-pattern>/userServlet2</url-pattern>-->
<!--
/ 表示通配所有资源,不包括jsp文件
/* 表示通配所有资源,包括jsp文件
/a/* 匹配所有以a前缀的映射路径
*.action 匹配所有以action为后缀的映射路径
-->
<!-- <url-pattern>/*</url-pattern>-->
</servlet-mapping>
</web-app>
- Servlet并不是文件系统中实际存在的文件或者目录,所以为了能够请求到该资源,我们需要为其配置映射路径
- servlet的请求映射路径配置在web.xml中
- servlet-name作为servlet的别名,可以自己随意定义,见名知意就好
- url-pattern标签用于定义Servlet的请求映射路径
- 一个servlet可以对应多个不同的url-pattern
- 多个servlet不能使用相同的url-pattern
- url-pattern中可以使用一些通配写法
- / 表示通配所有资源,不包括jsp文件
- /* 表示通配所有资源,包括jsp文件
- /a/* 匹配所有以a前缀的映射路径
- *.action 匹配所有以action为后缀的映射路径
Servlet注解方式配置
@WebServlet注解的源码阅读
package jakarta.servlet.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @since Servlet 3.0
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
/**
* The name of the servlet
* 相当于 servlet-name
* @return the name of the servlet
*/
String name() default "";
/**
* The URL patterns of the servlet
* 如果只配置一个url-pattern ,则通过该属性即可,和urlPatterns属性互斥
* @return the URL patterns of the servlet
*/
String[] value() default {};
/**
* The URL patterns of the servlet
* 如果要配置多个url-pattern ,需要通过该属性,和value属性互斥
* @return the URL patterns of the servlet
*/
String[] urlPatterns() default {};
/**
* The load-on-startup order of the servlet
* 配置Servlet是否在项目加载时实例化
* @return the load-on-startup order of the servlet
*/
int loadOnStartup() default -1;
/**
* The init parameters of the servlet
* 配置初始化参数
* @return the init parameters of the servlet
*/
WebInitParam[] initParams() default {};
/**
* Declares whether the servlet supports asynchronous operation mode.
*
* @return {@code true} if the servlet supports asynchronous operation mode
* @see jakarta.servlet.ServletRequest#startAsync
* @see jakarta.servlet.ServletRequest#startAsync( jakarta.servlet.ServletRequest,jakarta.servlet.ServletResponse)
*/
boolean asyncSupported() default false;
/**
* The small-icon of the servlet
*
* @return the small-icon of the servlet
*/
String smallIcon() default "";
/**
* The large-icon of the servlet
*
* @return the large-icon of the servlet
*/
String largeIcon() default "";
/**
* The description of the servlet
*
* @return the description of the servlet
*/
String description() default "";
/**
* The display name of the servlet
*
* @return the display name of the servlet
*/
String displayName() default "";
}
@WebServlet注解使用
使用@WebServlet注解替换Servlet配置
@WebServlet(
name = "userServlet",
//value = "/user",
urlPatterns = {"/userServlet1","/userServlet2","/userServlet"},
initParams = {@WebInitParam(name = "encoding",value = "UTF-8")},
loadOnStartup = 6
)
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String encoding = getServletConfig().getInitParameter("encoding");
System.out.println(encoding);
// 获取请求中的参数
String username = req.getParameter("username");
if("atguigu".equals(username)){
//通过响应对象响应信息
resp.getWriter().write("NO");
}else{
resp.getWriter().write("YES");
}
}
}
3 Servlet生命周期
什么是Servlet的生命周期?
- 应用程序中的对象不仅在空间上有层次结构的关系,在时间上也会因为处于程序运行过程中的不同阶段而表现出不同状态和不同行为——这就是对象的生命周期。
- 简单的叙述生命周期,就是对象在容器中从开始创建到销毁的过程。
Servlet容器
Servlet对象是Servlet容器创建的,生命周期方法都是由容器(目前我们使用的是Tomcat)调用的。这一点和我们之前所编写的代码有很大不同。在今后的学习中我们会看到,越来越多的对象交给容器或框架来创建,越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上。
Servlet主要的生命周期执行特点
生命周期 | 对应方法 | 执行时机 | 执行次数 |
---|---|---|---|
构造对象 | 构造器 | 第一次请求或者容器启动 | 1 |
初始化 | init() | 构造完毕后 | 1 |
处理服务 | service(HttpServletRequest req,HttpServletResponse resp) | 每次请求 | 多次 |
销毁 | destory() | 容器关闭 | 1 |
生命周期总结
- 通过生命周期测试我们发现Servlet对象在容器中是单例的
- 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程
- 多个线程可能会使用相同的Servlet对象,所以在Servlet中,我们不要轻易定义一些容易经常发生修改的成员变量
- load-on-startup中定义的正整数表示实例化顺序,如果数字重复了,容器会自行解决实例化顺序问题,但是应该避免重复
- Tomcat容器中,已经定义了一些随系统启动实例化的servlet,我们自定义的servlet的load-on-startup尽量不要占用数字1-5
4 Servlet继承结构
Servlet 接口
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
- Servlet 规范接口,所有的Servlet必须实现
- public void init(ServletConfig config) throws ServletException;
- 初始化方法,容器在构造servlet对象后,自动调用的方法,容器负责实例化一个ServletConfig对象,并在调用该方法时传入
- ServletConfig对象可以为Servlet 提供初始化参数
- public ServletConfig getServletConfig();
- 获取ServletConfig对象的方法,后续可以通过该对象获取Servlet初始化参数
- public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
- 处理请求并做出响应的服务方法,每次请求产生时由容器调用
- 容器创建一个ServletRequest对象和ServletResponse对象,容器在调用service方法时,传入这两个对象
- public String getServletInfo();
- 获取ServletInfo信息的方法
- public void destroy();
- Servlet实例在销毁之前调用的方法
- public void init(ServletConfig config) throws ServletException;
GenericServlet 抽象类
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final long serialVersionUID = 1L;
// 初始化配置对象作为属性
private transient ServletConfig config;
// 构造器,为了满足继承而准备
public GenericServlet() {
}
// 销毁方法的平庸实现
public void destroy() {
}
// 获取初始参数的快捷方法
public String getInitParameter(String name) {
return this.getServletConfig().getInitParameter(name);
}
// 返回所有初始化参数名的方法
public Enumeration<String> getInitParameterNames() {
return this.getServletConfig().getInitParameterNames();
}
// 获取初始Servlet初始配置对象ServletConfig的方法
public ServletConfig getServletConfig() {
return this.config;
}
// 获取上下文对象ServletContext的方法
public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
}
// 获取Servlet信息的平庸实现
public String getServletInfo() {
return "";
}
// 初始化方法的实现,并在此调用了init的重载方法
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
// 重载init方法,为了让我们自己定义初始化功能的方法
public void init() throws ServletException {
}
// 打印日志的方法及重载
public void log(String message) {
ServletContext var10000 = this.getServletContext();
String var10001 = this.getServletName();
var10000.log(var10001 + ": " + message);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
// 服务方法再次声明
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
// 获取ServletName的方法
public String getServletName() {
return this.config.getServletName();
}
}
HttpServlet 抽象类
public abstract class HttpServlet extends GenericServlet {
// 用于定义常见请求方式名常量值
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
// 构造器,用于处理继承
public HttpServlet() {
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.cachedUseLegacyDoHead = Boolean.parseBoolean(config.getInitParameter("jakarta.servlet.http.legacyDoHead"));
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_get_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
protected long getLastModified(HttpServletRequest req) {
return -1L;
}
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (!DispatcherType.INCLUDE.equals(req.getDispatcherType()) && this.cachedUseLegacyDoHead) {
NoBodyResponse response = new NoBodyResponse(resp);
this.doGet(req, response);
if (req.isAsyncStarted()) {
req.getAsyncContext().addListener(new NoBodyAsyncContextListener(response));
} else {
response.setContentLength();
}
} else {
this.doGet(req, resp);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_put_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_delete_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String allow = this.getCachedAllowHeaderValue();
if (HttpServlet.TomcatHack.getAllowTrace(req)) {
if (allow.length() == 0) {
allow = "TRACE";
} else {
allow = allow + ", TRACE";
}
}
resp.setHeader("Allow", allow);
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String CRLF = "\r\n";
StringBuilder buffer = (new StringBuilder("TRACE ")).append(req.getRequestURI()).append(' ').append(req.getProtocol());
Enumeration<String> reqHeaderNames = req.getHeaderNames();
while(true) {
String headerName;
do {
if (!reqHeaderNames.hasMoreElements()) {
buffer.append(CRLF);
int responseLength = buffer.length();
resp.setContentType("message/http");
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
out.print(buffer.toString());
out.close();
return;
}
headerName = (String)reqHeaderNames.nextElement();
} while(this.isSensitiveHeader(headerName));
Enumeration<String> headerValues = req.getHeaders(headerName);
while(headerValues.hasMoreElements()) {
String headerValue = (String)headerValues.nextElement();
buffer.append(CRLF).append(headerName).append(": ").append(headerValue);
}
}
}
// 重载的service方法,被重写的service方法所调用,在该方法中,通过请求方式判断,调用具体的do***方法完成请求的处理
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} 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);
}
}
// 对服务方法的实现 在该方法中,将请求和响应对象转换成对应HTTP协议HttpServletRequest HttpServletResponse对象 调用重载的service方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException(lStrings.getString("http.non_http"));
}
this.service(request, response);
}
}
自定义Servlet
自定义Servlet中,必须要对处理请求的方法进行重写
- 要么重写service方法
- 要么重写doGet/doPost方法
5 ServletConfig和ServletContext
ServletConfig的使用
ServletConfig是什么
- 为Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象
- 容器会为每个Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性
ServletConfig是一个接口,定义了如下API
package jakarta.servlet;
import java.util.Enumeration;
public interface ServletConfig {
String getServletName();
ServletContext getServletContext();
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
}
方法名 | 作用 |
---|---|
getServletName() | 获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称 |
getServletContext() | 获取ServletContext对象 |
getInitParameter() | 获取配置Servlet时设置的『初始化参数』,根据名字获取值 |
getInitParameterNames() | 获取所有初始化参数名组成的Enumeration对象 |
使用方法
- 定义Servlet
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = this.getServletConfig();
// 根据参数名获取单个参数
String value = servletConfig.getInitParameter("param1");
System.out.println("param1:"+value);
// 获取所有参数名
Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
// 迭代并获取参数名
while (parameterNames.hasMoreElements()) {
String paramaterName = parameterNames.nextElement();
System.out.println(paramaterName+":"+servletConfig.getInitParameter(paramaterName));
}
}
}
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = this.getServletConfig();
// 根据参数名获取单个参数
String value = servletConfig.getInitParameter("param1");
System.out.println("param1:"+value);
// 获取所有参数名
Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
// 迭代并获取参数名
while (parameterNames.hasMoreElements()) {
String paramaterName = parameterNames.nextElement();
System.out.println(paramaterName+":"+servletConfig.getInitParameter(paramaterName));
}
}
}
- 配置Servlet
<servlet>
<servlet-name>ServletA</servlet-name>
<servlet-class>com.atguigu.servlet.ServletA</servlet-class>
<!--配置ServletA的初始参数-->
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>value2</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>ServletB</servlet-name>
<servlet-class>com.atguigu.servlet.ServletB</servlet-class>
<!--配置ServletB的初始参数-->
<init-param>
<param-name>param3</param-name>
<param-value>value3</param-value>
</init-param>
<init-param>
<param-name>param4</param-name>
<param-value>value4</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ServletA</servlet-name>
<url-pattern>/servletA</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletB</servlet-name>
<url-pattern>/servletB</url-pattern>
</servlet-mapping>
ServletContext的使用
ServletContext是什么
- ServletContext对象有称呼为上下文对象,或者叫应用域对象(后面统一讲解域对象)
- 容器会为每个app创建一个独立的唯一的ServletContext对象
- ServletContext对象为所有的Servlet所共享
- ServletContext可以为所有的Servlet提供初始配置参数
ServletContext怎么用
- 配置ServletContext参数
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<context-param>
<param-name>paramA</param-name>
<param-value>valueA</param-value>
</context-param>
<context-param>
<param-name>paramB</param-name>
<param-value>valueB</param-value>
</context-param>
</web-app>
- 在Servlet中获取ServletContext并获取参数
package com.atguigu.servlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从ServletContext中获取为所有的Servlet准备的参数
ServletContext servletContext = this.getServletContext();
String valueA = servletContext.getInitParameter("paramA");
System.out.println("paramA:"+valueA);
// 获取所有参数名
Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
// 迭代并获取参数名
while (initParameterNames.hasMoreElements()) {
String paramaterName = initParameterNames.nextElement();
System.out.println(paramaterName+":"+servletContext.getInitParameter(paramaterName));
}
}
}
6 HttpServletRequest和HttpServletResponse
HttpServletRequest简介
- HttpServletRequest是一个接口,其父接口是ServletRequest
- HttpServletRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service方法时传入
- HttpServletRequest代表客户端发来的请求,所有请求中的信息都可以通过该对象获得
HttpServletRequest常见API
- 获取请求行信息相关(方式,请求的url,协议及版本)
API | 功能解释 |
---|---|
StringBuffer getRequestURL(); | 获取客户端请求的url |
String getRequestURI(); | 获取客户端请求项目中的具体资源 |
int getServerPort(); | 获取客户端发送请求时的端口 |
int getLocalPort(); | 获取本应用在所在容器的端口 |
int getRemotePort(); | 获取客户端程序的端口 |
String getScheme(); | 获取请求协议 |
String getProtocol(); | 获取请求协议及版本号 |
String getMethod(); | 获取请求方式 |
- 获得请求头信息相关
API | 功能解释 |
---|---|
String getHeader(String headerName); | 根据头名称获取请求头 |
Enumeration getHeaderNames(); | 获取所有的请求头名字 |
String getContentType(); | 获取content-type请求头 |
- 获得请求参数相关
API | 功能解释 |
---|---|
String getParameter(String parameterName); | 根据请求参数名获取请求单个参数值 |
String[] getParameterValues(String parameterName); | 根据请求参数名获取请求多个参数值数组 |
Enumeration getParameterNames(); | 获取所有请求参数名 |
Map<String, String[]> getParameterMap(); | 获取所有请求参数的键值对集合 |
BufferedReader getReader() throws IOException; | 获取读取请求体的字符输入流 |
ServletInputStream getInputStream() throws IOException; | 获取读取请求体的字节输入流 |
int getContentLength(); | 获得请求体长度的字节数 |
- 其他API
API | 功能解释 |
---|---|
String getServletPath(); | 获取请求的Servlet的映射路径 |
ServletContext getServletContext(); | 获取ServletContext对象 |
Cookie[] getCookies(); | 获取请求中的所有cookie |
HttpSession getSession(); | 获取Session对象 |
void setCharacterEncoding(String encoding) ; | 设置请求体字符集 |
HttpServletResponse简介
- HttpServletResponse是一个接口,其父接口是ServletResponse
- HttpServletResponse是Tomcat预先创建的,在Tomcat调用service方法时传入
- HttpServletResponse代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息
HttpServletResponse的常见API
- 设置响应行相关
API | 功能解释 |
---|---|
void setStatus(int code); | 设置响应状态码 |
- 设置响应头相关
API | 功能解释 |
---|---|
void setHeader(String headerName, String headerValue); | 设置/修改响应头键值对 |
void setContentType(String contentType); | 设置content-type响应头及响应字符集(设置MIME类型) |
- 设置响应体相关
API | 功能解释 |
---|---|
PrintWriter getWriter() throws IOException; | 获得向响应体放入信息的字符输出流 |
ServletOutputStream getOutputStream() throws IOException; | 获得向响应体放入信息的字节输出流 |
void setContentLength(int length); | 设置响应体的字节长度,其实就是在设置content-length响应头 |
- 其他API
API | 功能解释 |
---|---|
void sendError(int code, String message) throws IOException; | 向客户端响应错误信息的方法,需要指定响应码和响应信息 |
void addCookie(Cookie cookie); | 向响应体中增加cookie |
void setCharacterEncoding(String encoding); | 设置响应体字符集 |
MIME类型
- MIME类型,可以理解为文档类型,用户表示传递的数据是属于什么类型的文档
- 浏览器可以根据MIME类型决定该用什么样的方式解析接收到的响应体数据
- 可以这样理解: 前后端交互数据时,告诉对方发给对方的是 html/css/js/图片/声音/视频/… …
- tomcat/conf/web.xml中配置了常见文件的拓展名和MIMIE类型的对应关系
- 常见的MIME类型举例如下
文件拓展名 | MIME类型 |
---|---|
.html | text/html |
.css | text/css |
.js | application/javascript |
.png /.jpeg/.jpg/… … | image/jpeg |
.mp3/.mpe/.mpeg/ … … | audio/mpeg |
.mp4 | video/mp4 |
.m1v/.m1v/.m2v/.mpe/… … | video/mpeg |
7 请求转发和响应重定向
概述
什么是请求转发和响应重定向
-
请求转发和响应重定向是web应用中间接访问项目资源的两种手段,也是Servlet控制页面跳转的两种手段
-
请求转发通过HttpServletRequest实现,响应重定向通过HttpServletResponse实现
-
请求转发生活举例: 张三找李四借钱,李四没有,李四找王五,让王五借给张三
-
响应重定向生活举例:张三找李四借钱,李四没有,李四让张三去找王五,张三自己再去找王五借钱
请求转发
请求转发运行逻辑图
请求转发特点(背诵)
- 请求转发通过HttpServletRequest对象获取请求转发器实现
- 请求转发是服务器内部的行为,对客户端是屏蔽的
- 客户端只发送了一次请求,客户端地址栏不变
- 服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源
- 因为全程只有一个HttpServletRequset对象,所以请求参数可以传递,请求域中的数据也可以传递
- 请求转发可以转发给其他Servlet动态资源,也可以转发给一些静态资源以实现页面跳转
- 请求转发可以转发给WEB-INF下受保护的资源
- 请求转发不能转发到本项目以外的外部资源
测试代码
ServletA
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求转发器
// 转发给servlet ok
RequestDispatcher requestDispatcher = req.getRequestDispatcher("servletB");
// 转发给一个视图资源 ok
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("welcome.html");
// 转发给WEB-INF下的资源 ok
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/views/view1.html");
// 转发给外部资源 no
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.atguigu.com");
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 向请求域中添加数据
req.setAttribute("reqKey","requestMessage");
// 做出转发动作
requestDispatcher.forward(req,resp);
}
}
- ServletB
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 获取请求域中的数据
String reqMessage = (String)req.getAttribute("reqKey");
System.out.println(reqMessage);
// 做出响应
resp.getWriter().write("servletB response");
}
}
ServletA将请求转发给ServletB了。
响应重定向
响应重定向运行逻辑图
响应重定向特点(背诵)
- 响应重定向通过HttpServletResponse对象的sendRedirect方法实现
- 响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,是在服务端提示下的,客户端的行为
- 客户端至少发送了两次请求,客户端地址栏是要变化的
- 服务端产生了多对请求和响应对象,且请求和响应对象不会传递给下一个资源
- 因为全程产生了多个HttpServletRequset对象,所以请求参数不可以传递,请求域中的数据也不可以传递
- 重定向可以是其他Servlet动态资源,也可以是一些静态资源以实现页面跳转
- 重定向不可以到给WEB-INF下受保护的资源
- 重定向可以到本项目以外的外部资源
响应重定向测试代码
- ServletA
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 向请求域中添加数据
req.setAttribute("reqKey","requestMessage");
// 响应重定向
// 重定向到servlet动态资源 OK
resp.sendRedirect("servletB");
// 重定向到视图静态资源 OK
//resp.sendRedirect("welcome.html");
// 重定向到WEB-INF下的资源 NO
//resp.sendRedirect("WEB-INF/views/view1");
// 重定向到外部资源
//resp.sendRedirect("http://www.atguigu.com");
}
}
- ServletB
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 获取请求域中的数据
String reqMessage = (String)req.getAttribute("reqKey");
System.out.println(reqMessage);
// 做出响应
resp.getWriter().write("servletB response");
}
}
8 MVC架构模式
MVC(Model View Controller)是软件工程中的一种**
软件架构模式
,它把软件系统分为模型
、视图
和控制器
**三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
-
M:Model 模型层,具体功能如下
- 存放和数据库对象的实体类以及一些用于存储非数据库表完整相关的VO对象
- 存放一些对数据进行逻辑运算操作的的一些业务处理代码
-
V:View 视图层,具体功能如下
- 存放一些视图文件相关的代码 html css js等
- 在前后端分离的项目中,后端已经没有视图文件,该层次已经衍化成独立的前端项目
-
C:Controller 控制层,具体功能如下
- 接收客户端请求,获得请求数据
- 将准备好的数据响应给客户端
MVC模式下,项目中的常见包
-
M:
- 实体类包(pojo /entity /bean) 专门存放和数据库对应的实体类和一些VO对象
- 数据库访问包(dao/mapper) 专门存放对数据库不同表格CURD方法封装的一些类
- 服务包(service) 专门存放对数据进行业务逻辑运算的一些类
-
C:
- 控制层包(controller)
-
V:
- web目录下的视图资源 html css js img 等
- 前端工程化后,在后端项目中已经不存在了
非前后端分离的MVC
前后端分离的MVC