第三章 Servlet入门
- 客户端(浏览器)发送的数据会被提交到服务器;
- 服务器必须具备:1.接收请求数据;2.处理请求数据(业务逻辑的处理);3.给浏览器生成响应信息的能力;
- tomcat服务器遵从了JavaEE规范,Sun公司制定的动态资源规范Servlet实现这个功能;(运行在服务器端,能够接收用户请求,处理请求数据和给浏览器生成响应信息)
- 在java的世界里制定规范往往是接口来实现的,所以Servlet是一个接口,里边只是定义了一些方法;
- 如果,我们对这个接口进行具体的实现,就能够完成对用户数据的接收,处理和生成响应信息;
1、什么是Servlet
Servlet 是Server applet 简称,表示服务程序.是运行在服务端的Java小程序,是sun公司提供一套规范,用来处理客户端请求、响应给浏览器的动态web资源。其实servlet的实质就是java代码,通过java的API动态的向客户端输出内容,并且从客户端接收数据。
Servlet 的作用:
- 接收请求 :接收客户端发送的请求数据;
- 业务逻辑的处理;
- 响应结果 :将处理结果响应给客户端(浏览器);
2、Servlet入门开发步骤和代码案例
之前我们一直学习将html页面发布到tomcat服务器中,然后用户通过浏览器访问html页面,其实用户也可以通过
浏览器访问java的类,要想让用户能够访问到我们书写的java类,可以按照如下做法:
1.java类希望让用户通过浏览器访问,需要将被访问的类实现规范(接口),这个规范(接口)称为Servlet。
这里不需要导入jar包,因为tomcat软件已经集成。
2.创建一个java类实现Servlet接口
3.由于Servlet属于接口,肯定有抽象方法,所以要求自定义实现类实现接口中的所有抽象方法
主要是Servlet接口中的service方法,service方法主要是用来处理来自客户端的服务的
4.操作完成之后,客户端要想访问该类,必须使用Tomcat服务器帮助我们发布项目,需要通知tomcat,已经创建好了
一个类,可以供外部访问
代码如下:
public class HelloWorldServlet implements Servlet{
/*
处理客户端的服务
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service......");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {}
@Override
public ServletConfig getServletConfig() {return null;}
@Override
public String getServletInfo() { return null;}
@Override
public void destroy() {}
}
5.发布项目,启动项目,使用浏览器访问这个类;
在浏览器中输入:http://localhost:8080/HelloWorldServlet
如上图所示,按照上述访问方式我们是访问不到类的。 那么怎么才能让浏览器客户端访问到我们自己书写的类呢? 将我们书写的类配置到web项目的核心配置文件web.xml中就可以实现客户端访问到类。
那么需要在web.xml中书写如下内容:
<servlet>
<servlet-name>helloWorldServlet</servlet-name>
<servlet-class>com.ithea.sh.quickstart_01.HelloWorldServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloWorldServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
【注意】 url-pattern 映射路径前面必须添加"/";
对上述标签的解释如下所示:
<servlet>// 将自定义类注册给tomcat,其实就是通知tomcat,有一个类可以被外部访问
<servlet-name></servlet-name> //类的一个别名,随便写,但是必须保证在当前web.xml文件中唯一
<servlet-class></servlet-class> //全限定类名 ,tomcat获得类可以帮助我们实例化对象,调用类中的方法。tomcat底层使用反射创建对象
//对象.方法
<servlet-mapping> //映射路径,给访问的类加路径
<servlet-name></servlet-name> //上面配置的别名,必须匹配已经存在的名称
<url-pattern></url-pattern> //被访问的路径,浏览器最后输入的访问路径,
例如:http://localhost:8080/hello
访问路径为:http://ip:8080/hello
【注意事项】
使用idea创建的web项目,可以通过配置,访问路径中不需要添加项目名,具体配置如下:
3、Servlet执行流程分析
之前的java项目中,我们运行一段程序都是使用main方法来运行的。Servlet中没有main方法,那它是怎么运行的呢?
说明:
1.当我们点击run运行的时候,tomcat之所以会启动,是因为程序入口(main方法)在tomcat中
2.tomcat开始运行,会加载web项目里面的配置文件web.xml(xml解析,读取数据)
主要是根据url-pattern 找到对应的servlet-class
3.然后tomcat进入等待状态(永不停止,除非手动关闭)
4.当用户在浏览器中输入地址:http://localhost:8080/hello就会定位到tomcat的访问的项目下面的某个servlet中
5.tomcat会根据 /hello 的servlet的虚拟路径 找到HelloServlet的全限定名
6.tomcat底层通过反射创建HelloServlet的对象,并调用HelloServlet的service方法:
Class clazz = Class.forName("全限定名");
Servlet servlet = clazz.newInstance();//实际上HelloServlet对象,向上转型
servlet.service();
4、Servlet编程优化
我们发现在上述的HelloWorldServlet中,实现Servlet接口后能够接收浏览器发送的请求。由于Servlet是一个接口,我们实现接口后必须重写接口中所有的抽象方法。但是,我们在接口请求和响应数据的时候只需要一个service方法就足够了。所以,我们需要有选择地实现父接口中的方法。那么我们怎样做才能实现使用哪个方法就重写哪个方法呢?
我们可以在定义一个类GenericServlet来实现Servlet接口,这个类重写接口中的所有的抽象方法,然后让我们自定义类继承GenericServlet类,这样就可以只重写需要的方法了
4.1 方案一:GenericServlet优化方案
(1) GenericServlet简介
GenericServlet类是一个抽象类,它实现了多个接口,其中有一个是Servlet,所以它重写了Servlet接口中的所有方法。我们只需要继承GenericServlet类,重写其中的service方法即可。
(2)为什么要继承GenericServlet
- GenericServlet实现了Servlet接口,我们继承GenericServlet之后相当于间接实现了Servlet接口;
- GenericServlet中给我们提供了很多通用方法,这些方法已经帮我们实现了很多功能,继承GenericServlet之后可以直接调用这些方法,所以说,GenericServlet相对于Servlet,进行了功能的扩展和衍生;
- GenericServlet是一个抽象类,里边只有一个抽象方法service(),我们继承GenericServlet之后只需要重写service()方法处理具体的业务逻辑即可,减少了直接实现Servlet接口带来的代码冗余;
(3) 案例:使用GenericServlet优化Servlet接口编程
需求分析 :使用GenericServlet重写入门案例
技术分析 :
1. 创建一个普通的java类继承GenericServlet;
2. 在web.xml中配置这个GenericServlet的子类;
3. 重写service方法,在service方法中完成接收从浏览器请求数据的功能;
实现步骤 :
- 编写一个普通的java类,ServletDemo1,继承GenericServlet;
package com.hea.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* @author suoge
*/
public class ServletDemo1 extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
}
}
- 在web.xml配置ServletDemo1;
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>com.hea.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
- 重写service方法,并在service方法中书写以下代码,完成接收从浏览器请求数据的功能;
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("GenericServlet...");
}
- 启动tomcat并访问:
4.2 方案二:HttpServlet优化方案
问题:
在实际的生活中,客户端(浏览器)给我们发送的请求往往是HTTP协议下的请求,所以我们只需要处理HTTP的请求和响应即可。也就是说我们需要的只是一个与HTTP协议相关的Servlet。
解决方案:
Sun公司为我们提供了HttpServlet,对GenericServlet再次进行扩展和功能加强。
实际开发中:我们编写Servlet就可以采用 继承HttpServlet来完成servlet的开发,这样的Servlet我们可以获取更多的业务功能。
(1)HttpServlet简介:
HttpServlet是GenericServlet的一个子类。这个类是专门帮我们处理与HTTP协议相关的请求与响应的一个Servlet类,它里边的方法如下:
因为,在我们的日常生活中最常用的HTTP请求只有get请求和post请求。所以我们在继承了HttpServlet之后只需要重写里边的doGet()方法和doPost()方法即可满足我们的需求:
(2)为什么要继承HttpServlet
- HttpServlet继承了GenericServlet,功能比GenericServlet更强大;
- GenericServlet中处理不同的请求都使用service()方法,HttpServlet根据不同的请求方式提供了不同的方法进行处理。等于是细化了service()方法;
- HttpServlet类中的方法参数为HttpServletRequest对象和HttpServletResponse对象。使用这两个对象的好处在于:
- HttpServletRequest接口继承了ServletRequest接口,HttpServletResponse接口继承了ServletResponse接口。功能更强大;
- HttpServletRequest接口和HttpServletResponse接口为我们提供了很多处理HTTP协议相关的API,HTTP协议正是我们日常生活中使用得比较多的一种协议;
(3)HttpServlet编程
【Servlet优化二】HttpServlet编程
/**
* @author suoge
*/
public class FormServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("处理get请求提交的数据.................");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("处理post请求提交的数据..................");
}
}
(4)请求是怎么到达doGet()和doPost()的
在我们继承HttpServlet之后,重写了doGet()和doPost()方法。浏览器发送的get请求会被doGet()接收并处理,浏览器发送的post请求会被doPost()请求接收并处理。服务器端是怎么辨别我的请求是get还是post请求的呢?下面,我们看一下HttpServlet的源码中的service方法:
- 实际处理请求的还是HttpServlet的service()方法;
- service方法中,接收请求之后获取了请求方式,
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获取数据的请求方式
String method = req.getMethod();
//根据请求方式的不同,调用不同的方法
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
//调用doGet()方法
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
//调用doPost()方法
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
说明:HttpServletRequest接口中的方法getMethod()可以获取用户的请求方式:
get请求:该方法获取字符串“GET”
post请求:该方法获取字符串“POST”
自定义HttpServlet的代码:
public abstract class HttpServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//强制类型转换
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
this.service(request,response);
}
private void service(HttpServletRequest request, HttpServletResponse response) {
//获取请求方式
String method = request.getMethod();//GET POST
if("GET".equals(method))
{
//get
doGet(request,response);
}else if("POST".equals(method))
{
//post
doPost(request,response);
}else
{
//其他请求
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
}
}
HttpServlet类的子类:
public class FormServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
//post请求执行的方法
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
//get请求执行的方法
System.out.println("get.....");
}
}
4.3 Servlet编程优化小结
- Servlet编程的作用:接收浏览器的请求,处理请求数据,给浏览器响应数据;
- Servlet接口:
- Sun公司规定如果想要在服务端接收和响应客户端的数据,必须实现Servlet接口;
- 由于Servlet是一个接口,实现Servlet接口后必须重写其中所有的抽象方法;
- GenericServlet:
- GenericServlet:是一个抽象类,它实现了Servlet接口,与协议无关;
- 继承GenericServlet,只需要重写service方法即可接收和响应数据;
- HttpServlet:
- HttpServlet:是一个抽象类,它继承了GenericServlet,为HTTP请求定制的;
- 继承HttpServlet,重写doGet和doPost方法,能够更加方便地处理不同请求的数据;
总结:
方案二优化总结:
1.用户访问服务端都是http协议,使用HttpServlet专门来处理http协议的请求与响应,我们希望使用HttpServlet类的方法中的HttpServletRequest和HttpServletResponse,我们需要子接口,而之前给的是父接口ServletRequest和ServletResponse,所以需要强制类型转换。
2.之前无论是get还是post请求都是在service方法中处理,我们希望使用原子性思想,即如果是get请求就执行get方法,如果是post请求就执行post方法。对请求方式进行细分:
通过HttpServletRequest接口中的getMethod()方法来获取请求方式:
get请求,该方法获取GET字符串
post请求,该方法获取POST字符串
4.4 经验值分享
① 响应状态码405
请求方法没有重写.....
/*
注意: 如果我们不重写doGet/doPost方法, 那么父类的doGet/doPost方法会执行(继承)
给浏览器响应一个错误: 状态码405 (http1.1)
*/
② 响应状态码500
java代码写错了...
5、Servlet生命周期
生命周期指的是一个对象从创建到销毁的过程。我们的Servlet是由谁创建的,何时创建的,创建之后做了哪些工作,工作完成之后又是何时销毁的呢?下面我们就来看一下Servlet的生命周期。
1. 谁创建:tomcat创建;
2. 何时创建:启动tomcat之后,在浏览器地址栏中第一次访问这个Servlet的时候,tomcat使用反射技术创建了当前访问servlet的对象。当前servlet对象只会被创建一次,创建完成后使用对象调用init()方法来初始化当前servlet;
3. 创建之后做了哪些工作:每次请求到这个Servlet时,由service方法处理请求和响应信息;
4. 何时销毁:服务器正常关闭,销毁前会调用destory()方法;
注意:
1、tomcat创建servlet的对象,使用反射技术调用的是自定义servlet中的无参构造方法,所以自定义servlet中必须含有无参构造方法
2、初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,如建立数据库的连接,获取配置信息等。对于每一个Servlet实例,init()方法只被调用一次。(这里我们先了解)
3、当关闭tomcat服务器的时候,tomcat就会使用servlet的对象调用servlet的destroy()方法,以便让该实例可以释放它所使用的资源,保存数据到持久存储设备中,该实例随后会被Java的垃圾收集器所回收。如果再次需要这个Servlet处理请求,Servlet容器会创建一个新的Servlet实例。
【测试代码】
public interface Servlet {
/*
* Servlet对象创建后,使用对象立刻调用init()方法,只调用一次
*/
public void init(ServletConfig config) throws ServletException;
/*
* Servlet对象创建后,每次的请求都由service方法处理
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
/*
* Servlet对象销毁前会调用destory方法。主要是用于释放一些资源。
*/
public void destroy();
}
【实现类】
package com.hea.user.web;
import javax.servlet.*;
import java.io.IOException;
public class LifeCycleTestServlet implements Servlet {
public LifeCycleTestServlet() {
System.out.println("创建对象执行构造方法");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init.....");
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service.....");
}
@Override
public void destroy() {
System.out.println("destroy.....");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
执行的结果如下:
【小结】
1. Servlet对象的创建:
1. 创建者:tomcat;创建时机:在Servlet第一次被访问的时候;
2. 特点:
1. 只会创建一次(单例);
2. 对象创建完成之后,会调用init()方法;
2. Servlet运行:
1. service()方法接收用户的请求,和处理响应;
2. 特点:
1. 每次对这个Servlet的访问都会由service()方法来处理;
2. service()方法的两个参数:request和response对象专门用来处理请求和响应;
3. Servlet销毁:
1. Servlet对象在服务器正常关闭的时候销毁;
2. 特点:
1. 销毁的时候会调用destory()方法;
6.服务器启动,立刻加载Servlet对象
问题
:普通的Servlet对象在我们第一次访问的时候创建, 开发中,如果我们需要在服务器启动的时候,初始化Servlet对象应该如何编写呢?
需求
:服务器tomcat启动,立刻加载配置文件,获取配置文件信息,为项目后续代码提供数据支持。
解决
:此时的业务场景,我们需要在web.xml文件中
给指定的Servlet添加一个标签<load-on-startup>
实现步骤
:在web.xml
中在**标签内部中配置**:
数值越小,优先级越高,开始时不止一个词servlet,所以使用优先级去决定让哪个servlet先执行
2 --- 传入正整数,整数越小,被创建的优先级就越高。12345
举例:上述我们在演示servlet的生命周期的时候,创建了LifeCycleServlet,如下:
public class LifeCycleTestServlet implements Servlet {
public LifeCycleTestServlet() {
System.out.println("创建对象执行构造方法");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init.....");
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service.....");
}
@Override
public void destroy() {
System.out.println("destroy.....");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
如果我们想在tomcat服务器一启动的时候就初始化servlet对象,我们可以在web.xml文件中按照如下配置:
<servlet>
<servlet-name>lifeCycleServlet</servlet-name>
<servlet-class>com.ithea.sh.lifecycle_04.LifeCycleServlet</servlet-class>
<!--优先级是2-->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>lifeCycleServlet</servlet-name>
<url-pattern>/life</url-pattern>
</servlet-mapping>
注意:
对于2 --- 传入正整数,整数越小,被创建的优先级就越高。12345
优先级越高,服务器启动的时候就越先被初始化。
7、 Servlet映射路径配置规范
在web.xml核心配置文件的标签:中的映射路径配置存在以下几种方式:
1. 完全匹配 /user/hello 资源路径为/user/hello时可以访问
2. 目录匹配 /user/* 资源路径中含有/user目录均可访问
3. 后缀名匹配 *.do 资源路径中以.do结尾的均可访问
4. 缺省路径 / 访问的路径找不到,就会去找缺省路径
tomcat获得匹配路径时,优先级顺序:1 >2 > 3 > 4
注意:开发中一般使用完全匹配,即一个Servlet对应一个映射路径。
1、完全路径匹配
完全路径匹配就是在映射路径这里配置什么就在浏览器地址栏访问什么。代码演示如下所示:
创建一个PathOneServlet继承GenericServlet,代码如下:
public class PathOneServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("PathOneServlet......");
}
}
web.xml配置文件中的代码:
<servlet>
<servlet-name>pathOneServlet</servlet-name>
<servlet-class>com.ithea.sh.pathservlet_05.PathOneServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathOneServlet</servlet-name>
<!--全路径访问-->
<url-pattern>/user/path</url-pattern>
</servlet-mapping>
注意:/user/path 这里书写的内容不固定,也就是说不一定是user或者path。随便定义。
浏览器地址栏访问:
2、目录匹配
目录匹配 :/user/*
说明:
1)当前路径以及子路径都可以访问到servlet。也就是说只要资源路径中含有/user目录均可访问servlet。
2)上述路径不一定是user,内容随便书写。
创建一个PathTwoServlet继承HttpServlet
public class PathTwoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("PathTwoServlet。。。");
}
}
web.xml配置文件中的代码:
<servlet>
<servlet-name>pathTwoServlet</servlet-name>
<servlet-class>com.ithea.sh.pathservlet_05.PathTwoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathTwoServlet</servlet-name>
<!--目录匹配访问-->
<url-pattern>/user/*</url-pattern>
</servlet-mapping>
浏览器地址栏访问:
只要在浏览器地址栏中输入的地址前面是http://localhost:8080/user,后面的内容随便写。
或者
http://localhost:8080/user/kajaja/alakal
idea的控制台输出结果:
PathTwoServlet。。。
PathTwoServlet。。。
3、后缀名匹配
后缀名匹配:*.do.只要资源路径中以.do结尾的均可访问servlet.
创建一个PathThreeServlet继承GenericServlet,代码如下所示:
public class PathThreeServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("PathThreeServlet....");
}
}
web.xml配置文件中的代码:
<servlet>
<servlet-name>pathThreeServlet</servlet-name>
<servlet-class>com.ithea.sh.pathservlet_05.PathThreeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathThreeServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
注意:上述路径*.do。不一定是do,其他的内容也可以。
浏览器访问:
http://localhost:8080/jahahah/alakk.do
idea的控制台如下所示:
PathThreeServlet....
4、缺省路径
缺省路径 : / . 如果在浏览器输入访问servlet的路径在web.xml中不存在那么就会去找缺省路径。
创建PathFourServlet类继承GenericServlet:
public class PathFourServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("PathFourServlet....");
}
}
web.xml配置文件中的代码:
<servlet>
<servlet-name>pathFourServlet</servlet-name>
<servlet-class>com.ithea.sh.pathservlet_05.PathFourServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathFourServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
浏览器访问:http://localhost:8080/jahahah/alakkkajaajaj
访问的路径不存在,则找缺省路径。
idea控制台输出内容:
PathFourServlet....
8、 相对/绝对路径
- 现阶段我们访问资源的方式越来越多,请求路径在编写时难免出现混淆
- 浏览器的地址栏 (输入服务器某个资源地址,敲回车会发送请求)
- a标签的href属性 (超链接被点击的时候会发送请求)
- form表单的action属性 (form中的submit按钮被点击的时候,发送请求)
- js的loation.href属性 (只要设置, 触发相应的)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>path</title>
</head>
<body>
<h3>测试访问路径:http://localhost:8080/day08_servlet_xml/static/path.html</h3>
<h4>绝对路径</h4>
<!--
在开发时,强烈建议使用绝对路径
完整:协议://域名:端口/映射路径名/资源名
推荐:/映射路径名/资源名 (省略了三要素,前提: 当前页面和所访问的资源必须在同一服务器上)
注意绝对路径最前面需要加/
-->
<a href="http://localhost:8080/day08_servlet_xml/quickServlet">带http协议的绝对路径:quickServlet</a> <br>
<a href="/day08_servlet_xml/quickServlet">不带http协议的绝对路径:quickServlet</a>
<h4>相对路径路径</h4>
<!--
相对路径语法:
./ 当前目录 注:./可以省略不写
../ 上级目录
-->
<a href="../quickServlet">相对路径:quickServlet</a>
</body>
</html>
说明:
对于相对路径不是看项目,而是看浏览器地址栏访问的资源路径。
举例:
假设我们在浏览器地址栏访问的页面路径:
http://localhost:8080/index.html
而在index.html页面想使用相对路径访问servlet:
http://localhost:8080/hello
那么对于上述两个路径而言只有最后一级目录不一样,所以在index.html中直接使用 ./hello 表示在当前路径下查找hello的servlet,当然了这里./可以省略不写 直接书写 <a href="hello">相对路径:helloServlet</a>
总结:开发中建议书写绝对路径,如果层级比较多,相对路径书写太麻烦并且容易产生错误。
第四章 Servlet3.0注解开发
在我们上面创建的Servlet中,Servlet的配置信息都是在web.xml中完成的。如果,我们创建的Servlet非常多,就会导致web.xml中的配置非常臃肿,不便于管理。Servlet3.0为我们提供了注解支持,创建Servlet的时候。在这个Servlet类上面添加注解就可以完成Servlet的配置。这样对于创建的servlet类就不用在web.xml配置文件中配置了。接下来我们就开始学习Servlet3.0提供的注解支持。
1、Servlet3.0新特性
-
注解支持
该版本新增了若干注解,用于简化 Servlet、过滤器(Filter)和监听器(Listener)的声明,这使得 web.xml 部署描述文件从该版本开始不再是必选的了。就是可以不再需要web.xml文件了。
2、为什么要使用注解
- web.xml中配置过多servlet不便于管理,容易出错;
- 注解开发使得开发更敏捷,效率更高;
- 注解开发是公司未来开发的一种趋势;
3、使用IDEA创建3.0版本的Servlet
新版的IDEA(2017版本)创建的Servlet默认是3.0版本的,所以我们只需要新建一个Servlet,然后用注解配置即可。具体步骤如下:
第一步:新建一个Servlet;
在包上面点击鼠标右键New-->Servlet
给这个Servlet取个名,然后点击OK
第二步:配置Servlet
创建完成后,Servlet类上面会默认添加一个注解@WebServlet(name="Servlet3Demo")
。这个@WebServlet
注解就是用来配置当前这个Servlet的。这个注解中常用的有两个属性:
- name属性: 相当于web.xml的 ;
- urlPatterns属性: 编写访问servlet的路径 类似于
所以,我们需要手动在@WebServlet
注解中添加urlPatterns
属性,来设置映射路径。
package com.hea.servlet;
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;
/**
* @author suoge
* 1. name属性: 相当于web.xml的 <servlet-name>;
* 2. urlPatterns属性: 编写访问servlet的路径 类似于 <url-pattern>
*/
@WebServlet(name = "Servlet3Demo",urlPatterns = "/servlet3")
public class Servlet3Demo extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
第三步:启动tomcat并访问
访问路径:http://localhost:8080/servlet3
;
运行结果:
其实到这里我们已经完成了servlet3.0的注解开发。但是对于我们使用注解开发,我们只需要在浏览器输入@WebServlet注解中的属性urlPatterns 的属性值即可,我们不需要name属性,所以我们可以在idea中更改下servlet生成注解的模板代码。
4、优化
4.1 优化一
基于IDEA的模板,快速创建Servlet。
【模板配置代码】
在idea中按照如下操作即可完成修改servlet的注解模板代码:
说明:打开设置Settings--->搜索框输入tempplate---->找到 File and code Templates---->到右边点击other---->到下面选择Web----->然后选择Servlet Annotated Calss.java---->到原来的模板中只需要修改两个地方,一个是在doPost方法体中加入:doGet(request,response); 来调用doGet方法。
另外一个是将
WebServlet(name = "${Entity_Name} ")
变为WebServlet("/${Entity_Name}")
注意:不要将下面的所有的代码都复制你的idea中,只是修改两个地方即可。
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
@javax.servlet.annotation.WebServlet("/${Entity_Name}")
public class ${Class_Name} extends javax.servlet.http.HttpServlet {
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
doGet(request,response);
}
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
}
}
4.2 优化二
在doPost方法中调用doGet方法。这样我们的代码都在goGet()方法里写就可以了。
说明:上述做法的好处是无论前端浏览器是get还是post请求,我们都可以在doGet方法中完成代码的书写。否则在开发的时候有可能会导致是get请求却在doPost方法中完成的代码,或者是post请求却在doGet方法中完成的代码,导致我们无法看到代码完成的效果。
5、Servlet开发最终版本
@WebServlet("/lastDemo")
public class LastDemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
小结:
1、Servlet3.0注解开发小结:
第一步:创建一个普通类继承HttpServlet,重写doGet和doPost方法;
第二步:在类上面添加注解 @WebServlet;
第三步:配置注解@WebServlet的值(映射路径);
2、@WebServlet注解配置简介:
【1】简单配置:@WebServlet("/annDemoServlet") 注解的默认值为value值,就等价于web.xml中<url-pattern>/annDemoServlet</url-pattern>;
【2】其他配置:@WebServlet(name = "AnnDemoServlet",urlPatterns = "/annDemoServlet") 其中的
name属性值等价于web.xml中<servlet-name>AnnDemoServlet</servlet-name>的值
urlPatterns的属性值等价于web.xml中<url-pattern>/annDemoServlet</url-pattern>的值
3、注意事项:web.xml配置方式和注解只能使用一种,以后统一使用注解开发。
WebServlet注解中的属性含义: