10.Servlet
10.1.介绍
- 运行在服务器端的小程序
- Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
- 将来我们自定义一个类,实现Servlet接口,或继承GenericServlet类,或继承HttpServlet类,重写方法。
- Servlet执行方式:反射
FirstServlet servlet = Class.forName(FirstServlet).newInstance(); servlet.service(request,response)
10.2.Servlet执行流程
- 以访问
http://localhost/app/temp
为例,简述整个请求的访问过程- 浏览器帮助生成一个HTTP请求报文,传输到服务器
- 被监听着80端口号的Connector接收到,将请求报文解析成为request对象,同时生成一个response对象
- 将这两个对象传给engine,engine进一步挑选合适的host,将对象进行进一步下发
- host的职责就是来挑选一个合适的Context(形成 应用名——应用映射关系),将这两个对象进行进一步下发
- Context内部根据/servlet1寻找对应的servlet-class,此时先判断当前servlet是否已经有了一个对象(判断当前请求是否是第一次访问某个servlet),如果没有,则通过反射实例化一个对象出来;如果有,则直接执行后面的操作
- 紧接着执行该servlet的service方法,同时将这两个对象作为参数传递进去,service方法执行(service方法中可以读取request里面的数据,以及对response做出相应的响应)
- Connector读取response里面的数据,生成HTTP响应报文
- JavaEE项目目录结构
- src
- web
- WEB-INF(屏蔽class文件,不能被浏览器直接访问到)
- web.xml配置文件
- index.jsp
- 其他自建文件
- WEB-INF(屏蔽class文件,不能被浏览器直接访问到)
10.3.编写Servlet的方式
10.3.1.配置web.xml
- 创建JavaEE项目,配置Tomcat
- 定义一个类,实现Servlet接口,或继承GenericServlet类,或继承HttpServlet类,
public class ServletDemo1 implements HttpServlet
- 实现接口中的抽象方法
- 实现Servlet接口,需要重写全部方法
- 继承GenericServlet抽象类,需要重写service方法。
- 继承HttpServlet抽象类,需要实现什么功能就重写什么方法。
- 配置Servlet
- 在web.xml中配置
<!--配置Servlet -->
<servlet>
<servlet-name>demo1</servlet-name> <!-- 名字称任取 -->
<servlet-class>servlet.ServletDemo1</servlet-class> <!-- 全类名 -->
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name> <!-- 必须和上面的名称一样 -->
<url-pattern>/demo1</url-pattern> <!-- 本质上,是通过同一个name把class和url-pattern关联在一起形成映射关系 -->
</servlet-mapping>
10.3.2.Servlet3.0注解配置
- 支持注解配置。可以不需要web.xml了。
- 步骤:
- 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
- 定义一个类,实现Servlet接口,或继承GenericServlet类,或继承HttpServlet类,
- 重写方法
- 在类上使用@WebServlet注解,进行配置
@WebServlet("资源路径") @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebServlet { String name() default "";//相当于<Servlet-name> String[] value() default {};//代表urlPatterns()属性配置 String[] urlPatterns() default {};//相当于<url-pattern> int loadOnStartup() default -1;//相当于<load-on-startup> WebInitParam[] initParams() default {}; boolean asyncSupported() default false; String smallIcon() default ""; String largeIcon() default ""; String description() default ""; String displayName() default ""; } //Example //@WebServlet(name = "CurrentTime", urlPatterns = "/temp") //因为我们在这个类上添加注释,name属性其实就是这个类,添加与不添加name属性意义不大 //@WebServlet(urlPatterns = "/temp") //到这一步之后,还可以精简到底,如果括号里面只有一个值,那么默认表示的就是urlPattern //urlPattern属性又名value属性,二者一样,仅仅是名称差别 @WebServlet("/temp") public class CurrentTime extends GenericServlet { @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException{ System.out.println(new Date()); //把时间打印在控制台上 servletResponse.getWriter().println(new Date()); //把时间打印在网页上 } } //其中注解里的/temp表示了当前的url-pattern //浏览器中输入localhost/Tomcat配置的路径名/temp即可看到打印出来的当前时间,刷新网页后时间也会刷新
个人理解:这个@WebServlet(“/temp”)注解配置,相当于在web.xml中做了如下配置:
<!--配置Servlet --> <servlet> <servlet-name>Demo</servlet-name> <!-- 名字称任取 --> <servlet-class>servlet.CurrentTime</servlet-class> <!-- 全类名 --> </servlet> <servlet-mapping> <servlet-name>Demo</servlet-name> <!-- 必须和上面的名称一样 --> <url-pattern>/temp</url-pattern> <!-- 本质上,是通过同一个name把class和url-pattern关联在一起形成映射关系 --> </servlet-mapping>
注意,用此理解后面的
<init-param></init-param>
标签和<context-param></context-param>
标签所在的位置,它们都是在<servlet></servlet>
标签内的。
10.4.Servlet的API
10.4.1.生命周期函数
- 被创建:执行init方法,只执行一次
默认情况下,第一次被访问时,Servlet被创建,而不是运行Tomcat程序的时候创建,且可以配置执行Servlet的创建时机。
- public void init(ServletConfig config):Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
- 多个用户同时访问时,可能存在线程安全问题。
- 解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对其修改值
- 在web.xml下的
<servlet>
标签下添加新的配置- 第一次被访问时创建
- 添加
<load-on-startup>
标签的值为负整数<load-on-startup>-1</load-on-startup>
- 添加
- 在服务器启动时创建
- 添加
<load-on-startup>
的值为非负整数<load-on-startup>0</load-on-startup>
- 添加
- 示例
<!--配置Servlet --> <servlet> <servlet-name>demo1</servlet-name> <!-- 名字称任取 --> <servlet-class>servlet.ServletDemo1</servlet-class> <!-- 全类名 --> <load-on-startup>0</load-on-startup> <!-- 该Servlet第一次被访问时创建 --> </servlet> <servlet-mapping> <servlet-name>demo1</servlet-name> <!-- 必须和上面的名称一样 --> <url-pattern>/demo1</url-pattern> <!-- 本质上,是通过同一个name把class和url-pattern关联在一起形成映射关系 --> </servlet-mapping>
- 第一次被访问时创建
- 在
@WebServlet
注解下配置- 在第一次被访问时创建
- 在@WebServlet里添加
loadOnStartup
属性的值为负整数
@WebServlet(value = "/temp" , loadOnStartup = -1)
- 在@WebServlet里添加
- 在服务器启动的时候创建
- 在@WebServlet里添加
loadOnStartup
属性的值为非负整数
@WebServlet(value = "/temp" , loadOnStartup = 0)
- 在@WebServlet里添加
- 示例
@WebServlet(value = "/temp" , loadOnStartup = 1) public class CurrentTime extends HttpServlet { @Override public void init() throws ServletException{ System.out.println("init"); } }
- 注意事项
- Servlet注解相关配置
- urlpartten:Servlet访问路径
- 一个Servlet可以定义多个访问路径 : @WebServlet({“/d4″,”/dd4″,”/ddd4”})
- 但是多个servlet不可以映射同一个url-pattern
- servlet的url-pattern只有两种写法:/xxxx *.后缀
- 路径定义规则:
- /xxx:路径匹配
- /xxx/xxx:多层路径,目录结构
- *.do:扩展名匹配
- 路径优先级
- /开头的url-pattern的优先级高于*.后缀
- 对于都是/开头的url-pattern,匹配程度越高(匹配的越多),优先级也越高
- 路径定义规则:
- Servlet注解相关配置
- 在第一次被访问时创建
- public void service(ServletRequest req,ServletResponse res):每次访问Servlet时,Service方法都会被调用一次。
- public void destroy():被销毁时执行destroy方法,只执行一次
- Servlet被销毁时执行。服务器关闭时,Servlet被销毁
- 只有服务器正常关闭时,才会执行destroy方法。
- destroy方法在Servlet被销毁之前执行,一般用于释放资源
10.5.2.其他API
- public ServletConfig getServletConfig():返回一个ServletConfig对象,其中包含此servlet的初始化和启动参数。返回的ServletConfig对象是传递给init方法的对象。此接口的实现负责存储ServletConfig对象,以便此方法可以返回它。实现此接口的GenericServlet类已经完成了此操作。
- public String getServletInfo():返回有关servlet的信息,如作者、版本和版权。返回的字符串应该是纯文本,而不是任何类型的标记(如HTML、XML等)。
10.6.IDEA与tomcat的关联
- IDEA会为每一个tomcat部署的项目单独建议一份配置文件
- 查看控制台的log:Using CATALINA_BASE:
"C:\Users\fickle\.IntelliJIdea2019.3\system\tomcat\Tomcat_8_5_59_JavaEE"
- 相当于在这个位置复制了完整的一份配置,也就是新的Tomcat
- 在其conf/Catalina/localhost下,可以找到一个部署项目名称.xml文件,指向部署根目录
<Context path="/app" docBase="D:\IdeaProject\JavaEE\out\artifacts\homework_war_exploded" /> <!-- 其中,homework是module名称,path是应用名,就是我们对Tomcat的设置 -->
- IDEA的项目(开发目录)和tomcat部署的web项目
- 开发目录和部署目录不是同一个目录,但是也不是毫无关联,因为开发目录里面web目录下的文件会在部署根目录里面出现
- 开发目录里面的java源代码文件,经过编译以后,也会出现在部署根目录的WEB-INF/classes目录下。
- 开发目录里面的文件会按照某种复制规则将文件复制到部署根目录里面去
- IDEA的web文件夹,做了如下的一个映射,其实是为了方便再部署根目录里面放置静态资源文件。
- 凡是在该web目录下出现的文件,最终在部署之前,都会原封不动地复制到部署根目录里面去。也就是说,如果希望在部署根目录里面新建一个1.html,只需要在这个web目录里面设置就可以了,因为再次项目部署的时候,它会把1.html给复制到部署根目录里面去。
- 本质就是开发目录里面的文件会拼凑在一起,凑到out/artifacts/xxxx目录下,然后将这个目录部署再tomcat里面。
10.7.JavaEE项目开发步骤
- 新建Tomcat的JavaEE项目
- File—>new—>Project,在弹出的窗口左侧选择Java Enterprise,在右侧的Application Server选项框里,点击其右侧的new,选择Tomcat,IDEA会自动识别版本,一般开发来说应该勾选下面的Web Application,点击next即可
- 将一个SE项目设置为EE项目
- 新建一个普通的SE项目,在这个SE项目里创建一个web文件夹(也可以改为其他名称),和src目录平级
- File—>Project Structure,选择Facets,点击+号,选择web选项,选中当前module,将右侧的Path路径的web名称改为和第一步创建的文件夹一样的名称,在下面的Web Resource Directory同样改成如上名称
- 同样在Project Structure,选择Artifacts,点击右侧+号,选择Web Application:Exploded并选择From module,选择当前module。
- 点击右上角Add Configuration,选择Tomcat Server,local,在右侧选择Deployment,点击右侧+号,把Artifacts添加进来,下面的Application是应用名,可以任意设置,会影响浏览器的访问地址。将其他IDEA工程关联Tomcat的时候,要设置Deployment选项卡,选择对应的module war exploded。
- Project Structure里选择Modules,右侧找到Dependencies选项卡,右侧+号,选择第二项Libraries,在弹出的对话框里选择Tomcat,确定即可
10.8.Servlet的体系结构
Servlet接口
|
GenericServlet抽象类,实现了Servlet接口
|
HttpServlet抽象类,继承了GenericServlet抽象类,已经重写了父类中的方法
- GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
- 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
- API:
- public String getInitParameter(String name) 返回包含命名初始化参数值的字符串,如果该参数不存在,则返回null。
- public Enumeration getInitParameterNames() 返回servlet初始化参数的名称作为字符串对象的枚举,如果servlet没有初始化参数,则返回空枚举。
- public ServletConfig getServletConfig() 返回此servlet的ServletConfig对象。
- public ServletContext getServletContext() 返回对运行此servlet的ServletContext的引用。
- public String getServletInfo() 返回有关servlet的信息,如作者、版本和版权。
- public String getServletName() 返回此servlet实例的名称。
- public void log(String msg) 将指定的消息写入servlet日志文件,并以servlet的名称作为前缀。
- public void log(String message, Throwable t) 将给定可抛出异常的解释性消息和堆栈跟踪写入servlet日志文件,并以servlet的名称作为前缀。
- public abstract void service(ServletRequest req, ServletResponse res) 由servlet容器调用以允许servlet响应请求。
- API:
- HttpServlet:对http协议的一种封装,简化操作
- 定义类继承HttpServlet
- 重写doGet/doPost方法
- protected void doDelete(HttpServletRequest req, HttpServletResponse resp) 由服务器调用(通过服务方法)以允许servlet处理删除请求。
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) 由服务器调用(通过服务方法)以允许servlet处理GET请求。
- protected void doHead(HttpServletRequest req, HttpServletResponse resp) 从受保护的服务方法接收HTTP HEAD请求并处理该请求。
- protected void doOptions(HttpServletRequest req, HttpServletResponse resp) 由服务器调用(通过服务方法)以允许servlet处理OPTIONS请求。
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) 由服务器调用(通过服务方法)以允许servlet处理POST请求。
- protected void doPut(HttpServletRequest req, HttpServletResponse resp) 由服务器调用(通过服务方法)以允许servlet处理PUT请求。
- protected void doTrace(HttpServletRequest req, HttpServletResponse resp) 由服务器调用(通过服务方法)以允许servlet处理TRACE请求。
- protected long getLastModified(HttpServletRequest req) 返回HttpServletRequest对象上次修改的时间,以毫秒为单位,自1970年1月1日午夜GMT起。
- protected void service(HttpServletRequest req, HttpServletResponse resp) 接收来自公共服务方法的标准HTTP请求,并将它们分派给此类中定义的doXXX方法。
- public void service(ServletRequest req, ServletResponse res) 将客户端请求分派到受保护的服务方法。
10.9.两个特殊的urlPattern
- 有两个特殊的url-pattern,一个是
/*
, 一个是/
//第一个实现类 @WebServlet("/*") public class Servlet1 extends HttpServlet { @Override public void init() throws ServletException{ System.out.println("/*"); } } //第二个实现类 @WebServlet("/") public class Servlet2 extends HttpServlet { @Override public void init() throws ServletException{ System.out.println("/"); } }
- 在设置完这两个urlPattern之后,无论访问jsp页面还是访问html页面,均无法访问到,浏览器输出的都是
/*
,即执行了Servlet1中的service方法。 - 如果把
/*
的注解注释掉,访问jsp页面以及html页面,发现“.jsp页面可以正常显示, 而
.html页面无法正常显示,且访问
.html浏览器显示的是
/` 。- 实际上访问jsp时,访问的是一个servlet。在tomcat的conf/web.xml文件中,这个xml文件和每个应用的xml文件之间的关系,可以理解为他们之间有继承关系,应用可以继承得到tomcat地web.xml里面的设置。
- 处理静态资源的servlet叫做缺省servlet,专门用来接收那些没有任何servlet可以处理的请求,就会交给缺省servlet来处理。
<servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> <init-param> <param-name>fork</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>xpoweredBy</param-name> <param-value>false</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet> <!-- The mappings for the JSP servlet --> <servlet-mapping> <servlet-name>jsp</servlet-name> <url-pattern>*.jsp</url-pattern> <url-pattern>*.jspx</url-pattern> </servlet-mapping>
解释:
- 系统调用流程:当发起一个访问请求时,首先判断有没有Servlet可以响应该请求
- 如果有就从中挑选一个优先级最高的来处理,
- 如果没有,继续判断是否重新设置了一个缺省的Servlet(urlpattern为/),
- 如果有,会调用这个自己设置的缺省的Servlet,
- 如果没有,那么就调用系统默认的缺省的Servlet去响应
- 对于这个现象:当配置了一个urlPattern为
/*
(假设其为Servlet1),另一个为/
时(假设其为Servlet2),那么,当我们访问.jsp
页面时,是有两个Servlet实现类可以处理该请求的,一个是web.xml中的urlPattern,他的urlpattern配置为*.jsp
,一个是我们自己配置的Servlet1,urlpattern配置为/*
,这时会选择一个级别最高的,即Servlet1的service函数会运行,访问或.html
页面时,只有Servlet1来处理。而把Servlet1注释掉后,访问.jsp
页面只有web.xml来处理了,而Servlet2相当于重新设置了一个缺省的urlPattern,覆盖掉了系统默认的缺省的Servlet,因此当访问.html
页面时,我们自己所设置的Servlet2会处理该请求,浏览器输出为/
,即执行了Servlet2中的service方法 。
10.10.ServletConfig和ServletContext
- ServletConfig:获取某个Servlet的初始化参数
- 对于web.xml配置文件中,在servlet标签下有<init-param></init-param>这样一对标签
<servlet> <servlet-name>ServletConfig</servlet-name> <servlet-class>practice.ConfigServlet1</servlet-class> <init-param> <param-name>fork</param-name> <param-value>false</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ServletConfig</servlet-name> <url-pattern>/config</url-pattern> </servlet-mapping>
- 这个标签主要用于参数初始化,且是key-value键值对形式,里面的参数可以通过ServletConfig对象来获取
- public String getInitParameter(String name):获取具有给定名称的初始化参数的值。返回包含初始化参数值的字符串,如果不存在,则为null
/* 设在web.xml中的<servlet>标签里有一对<init-param></init-param>标签 <init-param> <param-name>name</param-name> <param-value>zs</param-value> </init-param> */ public class ConfigServlet1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //只需要在当前servlet中先拿到config对象 //这个方法是从GenericServlet继承来的,可以直接调用; ServletConfig servletConfig = getServletConfig(); //调用config.getInitParameter(key) String name = servletConfig.getInitParameter("name"); //当我们访问web.xml中配置的urlpattern时,即在浏览器中输入localhost/tomcat的app名称/config,可以看到输出了zs System.out.println(name); } }
- ServletContext:获取全局化的初始化参数,该对象在每个应用中有且只有一个,通过ServletContext对象的引用都可以拿到数据或修改数据
- 概念:代表整个web应用,可以和程序的容器(服务器)来通信
- 生命周期:
- 创建:服务器启动
- 销毁:服务器销毁
- 作用域:整个web应用
- 获取:
- 通过request对象获取:request.getServletContext();
- 通过HttpServlet获取:this.getServletContext();
- 功能:
- 获取MIME类型:
- MIME类型:在互联网通信过程中定义的一种文件数据类型
- 格式: 大类型/小类型 text/html image/jpeg
- 获取:String getMimeType(String file)
- MIME类型:在互联网通信过程中定义的一种文件数据类型
- 域对象:共享数据
- setAttribute(String name,Object value)
- getAttribute(String name)
3. removeAttribute(String name)
* ServletContext对象范围:所有用户所有请求的数据
-
获取文件的真实(服务器)路径:String getRealPath(String path)
String b = context.getRealPath("/b.txt");//web目录下资源访问 System.out.println(b);
String c = context.getRealPath(“/WEB-INF/c.txt”);//WEB-INF目录下的资源访问
System.out.println(c); String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问 System.out.println(a);
- 对于某个web.xml配置文件中,在Servlet-mapping标签后面有<context-param></context-param>一对标签
<servlet> <servlet-name>ServletConfig</servlet-name> <servlet-class>practice.ConfigServlet1</servlet-class> <init-param> <param-name>fork</param-name> <param-value>false</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ServletConfig</servlet-name> <url-pattern>/config</url-pattern> </servlet-mapping> <context-param> <param-name>charset</param-name> <param-value>gbk</param-value> </context-param>
- 这个标签同样用于参数初始化,且是key-value键值对形式,里面的参数可以通过ServletContext对象来获取
@WebServlet("/context") public class ContextServlet1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //先拿到ServletContext对象 ServletContext servletContext = getServletContext(); //调用getInitParameter,方法同样继承于GenericServlet String charset = servletContext.getInitParameter("charset"); //当我们访问注解配置的urlpattern的时候,即在浏览器中输入localhost/tomcat的app名称/context,可以看到输出了gbk System.out.println(charset); } }
- context域
- 在程序运行时,动态生成的数据需要多个servlet之间进行共享,每个应用只有一个
- API:
- public void setAttribute(String name,Object obj):存储数据
- public Object getAttitude(String name):通过键获取值
- public void removeAttribute(String name):通过键移除键值对
@WebServlet("/domain1") public class DomainServlet1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //context域 //进行了一些操作,得到一个值,list、string、对象 String name = "zhangsan"; ServletContext servletContext = getServletContext(); //这个API相当于去给这个servletContext对象里面的map进行赋值 servletContext.setAttribute("username", name); } } @WebServlet("/domain2") public class DomainServlet2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext servletContext = getServletContext(); String username = (String) servletContext.getAttribute("username"); System.out.println(username); } } //先访问domain1,再访问domain2,即可打印zhangsan //对于这样一个场景,在一个Servlet类里重新init函数,进行一些初始化配置,并附加loadOnStratup=非负整数属性 //可以实现当Tomcat启动时,初始化配置好,用户访问时使用其他Servlet来处理,即可获取相关数据
- 获取MIME类型:
10.11.JavaEE项目的相对路径问题
- 在web文件夹里新建一个名为1.txt的文件,编写程序希望可以拿到部署根目录的1.txt的绝对路径
@WebServlet("/path") public class PathServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //希望可以拿到部署根目录下面的1.txt的absolutePath----file----inputStream File file = new File("1.txt"); System.out.println(file.exists()); // 在EE项目里调用相对路径,相对相对路径相对的是tomcat的bin // 工作目录其实是tomcat的bin目录,实际上是在bin目录下调用了jvm // 从本质上去看EE项目:tomcat是一个java程序,调用我们写的代码片段 System.out.println(file.getAbsolutePath()); /* 输出结果: false D:\apache-tomcat-8.5.59\bin\1.txt */ //获取部署根目录下面1.txt文件的绝对路径 -----file ServletContext servletContext = getServletContext(); //输入一个空字符串,返回当前部署根目录的绝对路径 //输入一个相对路径,返回当前部署根目录的绝对路径+提供的相对路径 //假设提供了一个1.html,返回的是docBase + /1.html //如果希望获取应用下面的任何一个文件的绝对路径,只需要给它传入一个相对部署根目录的一个相对路径即可 String realPath = servletContext.getRealPath("1.html"); System.out.println(realPath); //WEB-INF用来屏蔽浏览器的直接访问,不会屏蔽服务器自身的访问 String realPath1 = servletContext.getRealPath("WEB-INF/2.txt"); boolean exists = new File(realPath1).exists(); System.out.println(exists); } }
- 每个应用其实有两个属性,path是应用名,docBase是当前应用的绝对路径。docBase就是我们应用的部署根目录。
- 只需要提供一个相对部署根目录的相对路径,那么拼上前面docBase就可以拿到绝对路径了。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181057.html