参阅资料推荐
Tomcat--各个目录详解(二) - 啤酒大泡泡 - 博客园
JavaWeb开发重定向与转发_孤夜的博客-CSDN博客
早期的web应用文件是手动往tomcat文件夹下复制的,由于每改动一次代码就得重新往tomcat的目录下部署一次,后来集成到了IDEA中,只需要重新运行程序即可热启动。
Get请求:
虚拟目录说的是请求转发的时候设置的头
response.setHeader("Location" , " /xxxx/yyyy" );
下面看一个demo带你了解一下tomcat
Servelet demo
观察这个demo运行起来的页面
真正部署到tomcat中的是 out---->artifacts---->ServeletPractice_war_exploded
1、路径问题
如果想要访问相应的页面,只需在根路径“/”下访问文件名即可,另外jsp文件就是写html的文件。
http://localhost:port/index.jsp
http://localhost:port/index2.jsp
Web-INF中的文件客户端的浏览器是不能直接访问的
比如Servelet01.class字节码文件无法访问
http://localhost:8080/WEB-INF/classes/com/test/app/Servelet01.class
http://localhost:8080/WEB-INF/classes/com/test/app/Servelet01
均无法访问
2、通过映射的方式访问WEB-INF文件夹下的class字节码文件
客户端的浏览器不能直接访问servlet文件只能通过映射间接访问servlet
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">
<servlet>
<servlet-name>Servlet001</servlet-name>
<servlet-class>com.test.app.Servlet01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet001</servlet-name>
<url-pattern>/servlet</url-pattern>
</servlet-mapping>
</web-app>
访问的时候出现了中文乱码
去Servlet01.java文件的service方法中设置它的servletResponse.setContentType("text/html;charset=UTF-8"); //设置编码格式
servletRequest.setCharacterEncoding("UTF-8");//servlet收到正常编码可以System.out.println()数据
3、带参访问java文件资源
http://localhost:8080/servlet?id=1
在Servlet01.java中添加程序
String id=servletRequest.getParameter("id") ;
4、注解方式
@WebServlet("/servlet")
可以达到相同的效果
5、Servlet的生命周期
@WebServlet("/servlet")
public class Servlet01 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("Serverlet init");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Servlet service");
servletResponse.setContentType("text/html;charset=UTF-8");
PrintWriter writer =servletResponse.getWriter();
writer.write("hello! Client~!");
writer.write("你好,客户端!");
String id=servletRequest.getParameter("id");
writer.write("收到id="+id);
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("Servlet destroy");
}
}
init service destroy属于生命周期的方法
创建 使用 回收
c++中是这样的
java 由于有GC机制,你只需要创建对象使用即可,不需要考虑回收
回车
回到idea
多次回车访问http://localhost:8080/servlet
当第一次访问的时候会调用init方法,当有了对象后就不会再调用init了,会值接调用service方法
存在对象直接调用,不存在再去创建,有点类似于字符串常量池
只需要一个对象,反复去使用,相当于单例模式
当你关闭程序的时候,会调用destroy释放servlet对象
6、模拟收到请求后tomcat处理Servlet对象创建的过程
访问http:localhost:8080/servlet的时候 程序会到web.xml一解析拿到全类名,通过反射机制去创建对象
1、当浏览器访问Servlet的时候,Tomcat 会查询当前Servlet的实例化对象是否存在,如果不存在,则通过反射机制动态创建对象,如果存在,直接执行第3步。
2、调用init方法完成初始化操作。
3、调用service方法完成业务逻辑操作。
4、关闭Tomcat时,会调用destory方法,释放当前对象所占用的资源。Servlet 的生命周期方法:无参构造函数、init、service、destory
1、无参构造函数只调用一次,创建对象。
2、init 只调用一次,初始化对象。
3、service 调用N次,执行业务方法。
4、destory 只调用一次,卸载对象。
下面有一段代码,如果不讲servlet.jar引入到JavaSE环境中,就会报错,无法使用Servlet。
因为在javaSE环境中加载不了Servlet01 实现的那个implements Servlet会报错
所以去tomcat解压的目录下找到servlet.jar复制到当前工程下的lib目录下手动导入一下
public static void main(String[] args) {
// 模拟tomcat创建对象的过程
// http:localhost:8080/servlet到web.xml一解析拿到全类名,通过反射机制去创建对象
String str = "com.test.app.Servlet01";
Class clazz = null;
try {
clazz = Class.forName(str);
Constructor constructor = clazz.getConstructor();
System.out.println(constructor);//会报错
// 因为在javaSE环境中加载不了Servlet01 实现的那个implements Servlet报错了
// 去tomcat解压的目录下找到servlet.jar复制到当前工程下的lib目录下手动导入一下
// 打印出:public com.test.app.Servlet01()
Object object= constructor.newInstance();
System.out.println(object);// com.test.app.Servlet01@43556938
} catch (Exception e) {
e.printStackTrace();
}
}
Servlet01.java
package com.test.app;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
/**
* @author:
* @date:2022/1/4
* @description:
*/
@WebServlet("/servlet")
public class Servlet01 implements Servlet {
public Servlet01() {
System.out.println("Servlet01的无参构造函数,在init之前,肯定是先有对象再去初始化");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("Serverlet init");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Servlet service");
servletResponse.setContentType("text/html;charset=UTF-8");
PrintWriter writer = servletResponse.getWriter();
writer.write("hello! Client~!");
writer.write("你好,客户端!");
String id = servletRequest.getParameter("id");
writer.write("收到id=" + id);
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("Servlet destroy");
}
public static void main(String[] args) {
// 模拟tomcat创建对象的过程
// http:localhost:8080/servlet到web.xml一解析拿到全类名,通过反射机制去创建对象
String str = "com.test.app.Servlet01";
Class clazz = null;
try {
clazz = Class.forName(str);
Constructor constructor = clazz.getConstructor();
System.out.println(constructor);//会报错
// 因为在javaSE环境中加载不了Servlet01 实现的那个implements Servlet报错了
// 去tomcat解压的目录下找到servlet.jar复制到当前工程下的lib目录下手动导入一下
// 打印出:public com.test.app.Servlet01()
Object object= constructor.newInstance();
System.out.println(object);// com.test.app.Servlet01@43556938
} catch (Exception e) {
e.printStackTrace();
}
}
}
7、ServletConfig
该接口是用来描述Servlet的基本信息的。
getServletName()返回Servlet的名称,全类名(带着包名的类名)getlnitParameter(String key)
获取init参数的值(web.xml)
getlnitParameterNames()
返回所有的initParamter的name值,一般用作遍历初始化参数
getServletContext()
返回ServletContext对象,它是Serviet 的上 下又,wl 3eive-。
ServletConfig和ServletContext的区别:
ServletConfig作用于某个Servlet实例,每个Servlet都有对应的ServletConfig,ServletContext作用于整个Web应用,一个Web应用对应一个ServletContext,多个Servlet实例对应一个ServletContext。
public interface ServletConfig {
String getServletName();
ServletContext getServletContext();
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
}
获取servletName()
在init方法中,即可 String servletName=servletConfig.getServletName();
从web.xml读取参数
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">
<!--context的初始化参数配置从这对标签中读取-->
<context-param>
<param-name>param0</param-name>
<param-value>value0</param-value>
</context-param>
<servlet>
<servlet-name>Servlet001</servlet-name>
<servlet-class>com.test.app.Servlet01</servlet-class>
<init-param>
<param-name>userName</param-name>
<param-value>admin</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Servlet001</servlet-name>
<url-pattern>/servlet</url-pattern>
</servlet-mapping>
<!--<!–采用注解的方式映射资源,更加方便快捷–>-->
</web-app>
servlet.java(因为要从web.xml读取初始化参数,所以一定要把@WebServlet注释掉)
//@WebServlet("/servlet") 由于本次演示在web.xml中定义了此servlet的映射,这里先注释掉,否则重复映射了就。
public class Servlet01 implements Servlet {
public Servlet01() {
System.out.println("Servlet01的无参构造函数,在init之前,肯定是先有对象再去初始化");
}
public String userName = "";
@Override
public void init(ServletConfig servletConfig) throws ServletException {
//1
String servletName = servletConfig.getServletName();// com.test.app.Servlet01
//2
//程序加载的时候可以设定一些初始化的参数,和servletRequest.getParameter()不一样,人家是从浏览器地址栏获取参数的。
//ServletConfig.getInitParameter()是从web.xml获取参数的
//基于注解取不到这样的东西,另外它用的很少。
this.userName = servletConfig.getInitParameter("userName");
//3.1
Enumeration<String> enStr = servletConfig.getInitParameterNames();//Enumeration有点类似于set集合,只是过时了抛弃了。
//3.2
Enumeration enumeration = servletConfig.getInitParameterNames();
//4
//servletConfig倾向于Servlet的实例的配置
//servletContext倾向于全局的一个配置的信息
ServletContext servletContext = servletConfig.getServletContext();//servlet上下文,整个servlet的管理者
String servletContextName = servletContext.getServletContextName();
String contextPath = servletContext.getContextPath();
String serverInfo = servletContext.getServerInfo();
// ServletContext同样有getInitParameter和getInitParameterNames
//web.xml中
// <context-param>
// <param-name>param0</param-name>
// <param-value>value0</param-value>
// </context-param>
// String xxxx = servletContext.getInitParameter("xxxx");
//Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
System.out.println(servletContextName+"\n"+contextPath+"\n"+serverInfo);
System.out.println("Serverlet init");
System.out.println("\tServerlet Name:" + servletName);//Serverlet Name:Servlet001
System.out.println("\tServerlet Init Parame:" + this.userName);//Serverlet Init Parame:admin
System.out.println("\tServerlet ParameterName:" + enStr);
//遍历3.1 Enumeration<String> enStr
while (enStr.hasMoreElements()) {
String element = enStr.nextElement();
System.out.println("\tServerlet ParameterName:" + element);
System.out.println("\tServerlet ParameterVlaue:" + servletConfig.getInitParameter(element));
}
//遍历3.2 Enumeration enumeration
while (enumeration.hasMoreElements()) {
String element = (String) enumeration.nextElement();
System.out.println("\tServerlet ParameterName:" + element);
System.out.println("\tServerlet ParameterVlaue:" + servletConfig.getInitParameter(element));
}
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Servlet service");
servletResponse.setContentType("text/html;charset=UTF-8");
PrintWriter writer = servletResponse.getWriter();
writer.write("hello! Client~!");
writer.write("你好,客户端!");
String id = servletRequest.getParameter("id");
writer.write("收到id=" + id);
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("Servlet destroy");
}
public static void main(String[] args) {
// 模拟tomcat创建对象的过程
// http:localhost:8080/servlet到web.xml一解析拿到全类名,通过反射机制去创建对象
String str = "com.test.app.Servlet01";
Class clazz = null;
try {
clazz = Class.forName(str);
Constructor constructor = clazz.getConstructor();
System.out.println(constructor);//会报错
// 因为在javaSE环境中加载不了Servlet01 实现的那个implements Servlet报错了
// 去tomcat解压的目录下找到servlet.jar复制到当前工程下的lib目录下手动导入一下
// 打印出:public com.test.app.Servlet01()
Object object = constructor.newInstance();
System.out.println(object);// com.test.app.Servlet01@43556938
} catch (Exception e) {
e.printStackTrace();
}
}
}
问题:
解决方案:
8、Servlet层次结构
探究HttpServlet
/**
* @author:
* @date:2022/1/5
* @description: 了解HttpServlet
*/
@WebServlet("/servlet02")
public class Servlet02 extends HttpServlet {
//顶端接口------------------------>底端接口
//Servlet---->GenericServlet---->HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//地址栏发送的请求是get请求
resp.getWriter().write("HtteServlet doGet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如果想要客户端发送post请求,要么写一个表单发送,要么用postman等工具发送post请求
resp.getWriter().write("HtteServlet doPost");
}
}
/**
* @author:
* @date:2022/1/5
* @description: 山寨版的GenericServlet
*
* //顶端接口------------------------>底端接口
* //Servlet---->GenericServlet---->HttpServlet
*/
//本类:Servlet03=====手动写了一个GenericServlet,让Servlet04去继承,目的是模拟GenericServlet的用法
//@WebServlet("/servlet03")
public class Servlet03 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//在客户端中,无论get请求还是post请求
//http://localhost:port/servlet03
//请求的响应都是 hello world,它不会区分post请求和get请求
servletResponse.getWriter().write("hello world");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
/**
* @author:
* @date:2022/1/5
* @description: 山寨版的HttpServlet
* 继承自己写的山寨版的GenericServlet,哪个有用你就重写哪一个方法,不用向实现Servlet一样重写那么多方法,而且还可以实现
* //顶端接口------------------------>底端接口
* //Servlet---->GenericServlet---->HttpServlet
*/
@WebServlet("/servlet04")
public class Servlet04 extends Servlet03 {
/**
* @param: [servletRequest, servletResponse]
* @return: void
* @description: 山寨版的HttpServlet
* 本类的重点是重写service,证明继承了GenericServlet类后就不需要重写那么多方法了
* 当然这个GenericServlet是手动模拟的一个Servlet03作用和GenericServlet一样。
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取请求类型
String method = request.getMethod();//会返回GET或者POST
System.out.println(method);
switch (method) {
case "GET":
this.doGet(request, response);
break;
case "POST":
this.doPost(request, response);
break;
}
}
//这里是让父类做个分发
public void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
}
//这里是让父类做个分发
public void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
}
}
/**
* @author:
* @date:2022/1/5
* @description: 继承山寨版的HttpServlet,同样可以实现doGet和doPost
*/
@WebServlet("/servlet05")
public class Servlet05 extends Servlet04 {
@Override
public void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.setContentType("text/html;charset=UTF-8");
httpServletResponse.getWriter().write("山寨版HttpServlet doGet");
}
@Override
public void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.setContentType("text/html;charset=UTF-8");
httpServletResponse.getWriter().write("山寨版HttpServlet doPost");
}
}
Get请求
Post请求