Servlet的Web访问名称
一个Servlet的web访问名称可以不止一个,并且除了用web.xml配置之外,还可以使用注解的方式配置,现在主流的配置方式是使用注解,这是Servlet3.0的特性,注解的配置要比web.xml轻量、简单,但是不排除一些老工程仍然使用着web.xml的配置方式。
下面介绍一下,在注解上使用 * 通配符:
/admin/* 这种配置表示访问的名称必须是以admin/开头,但是后面的字符串可以任意,*作为一个匹配任意字符串的存在,以下使用实际代码示例演示一下这个用法:
运行结果:
控制台:
*.action 表示访问的名称后缀必须是action ,前缀可以是任意的字符串:
运行结果:
控制台:
Servlet家族
Servlet家族派生体系中的最高父类是Servlet接口,此接口定义了其实现类必须要重写的基本方法。然后就是GenericServlet类,这是一个抽象类,是一个通用的Servlet,它实现于Servlet接口,这个类相当于给Servlet家族的派生体系增加了一个分支,接着就是到了具备协议的HttpServlet类,这也是一个抽象类继承于GenericServlet类,不过此类没有定义抽象方法,这是带有http协议的Servlet,是专门针对http所开发出来的。
现在的网站访问几乎都是http协议或https协议,为什么不在父类上就定义好协议?如果这么干的话,就违反了设计模式的六大原则,在最高父类上就写死了带有哪些协议,那么以后衍生出别的协议或者需要使用别的协议的话,岂不是无法扩展,只能对父类进行修改?这在程序设计上是绝对不允许的,计模式的六大原则之一就是开放-封闭原则:对修改封闭—对扩展开放。
而且单一职责原则也不允许一个类包含太多不同的功能,因为这会产生不必要的高耦合性,一个类只做一件主要的事情。所以开发Servlet家族的工程师就比较聪明,使用了一个最高接口定义了所有Servlet类都必须要具备的基本方法,然后让子类去做实现,不同的协议只需要增加不同的子类即可,无需对父类进行修改,并且子类之间互相不依赖,这是迪米特法则,将所有类的依赖都抽象到一个接口父类上,这就是面向接口编程。
面向接口的概念就像修理电脑一样,电脑某个配件坏了,直接更换就可以了,不需要更换其他的配件。例如:内存条坏了,我就换根内存条就好了,我不需要换硬盘也不需要换主板。更换一个配件完全不会影响到其他的配件正常使用,这是因为它们之间都不互相依赖,就像子类互相不依赖一样,它们都只依赖一个接口,只要接口合适安装上去就可以使用了(扩展子类)。子类互相不依赖耦合性就低就不会互相影响,就好像让你修电脑好像很简单,基本上换个配件就可以了(毕竟都有防呆接口)。但是让你去修收音机就不是那么简单了,收音机的配件基本上都集成在一块线路板上,全是二极管、电阻、单片机什么的,除了专门修理的人员一般人是不会修的,这就是因为耦合性太高。如果子类之间也像线路板一样耦合性这么高,想想也知道一旦出问题了修改起来有多麻烦。
所以为什么不像电脑中的配件一样,将子类内部的复杂性封装起来,让它们都只依赖一个父类接口,出问题了只需要修改或更换某个子类就可以了,而且当要增加功能的时候,就直接扩展子类即可,不需要再进行其他的变动,就像我电脑要音质牛逼一点就加个独立声卡,想要网速快一些我可以加个网卡,这就是为什么要去面向接口编程,为什么开发Servlet家族的工程师能够将Servlet的灵活、扩展性做的这么好,这都是因为应用了面向接口编程。
以上用了一大堆废话说明了为什么要面向接口编程,和面向接口编程的好处,接下来介绍一下Servlet接口,GenericServlet、HttpServlet类中的主要方法(不是全部):
1. Servlet接口
Servlet接口中有三个主要的方法,分别是:
init(ServletConfig) 初始化方法,此方法在Servlet实例化对象后会被调用。
service(ServletRequest, ServletResponse) 服务,用于接收客户端请求的方法
destroy() 销毁方法
Servlet接口源码:
2. GenericServlet抽象类
有四个主要的方法,分别是:
init(ServletConfig) 初始化方法,同样的初始化时会被调用
init() 初始化方法
service(ServletRequest, ServletResponse) 服务方法
destroy() 销毁方法
GenericServlet中有一个init(),这个方法是用来给用户重写的,通过重写这个方法我们可以在初始化时进行一些操作。其实这个方法会被init(ServletConfig)方法调用,所以才能在初始化时进行一些操作,看一下源码就知道了:
3. HttpServlet抽象类
HttpServlet的方法就比较多了,其中主要的是以下这些方法:
service(ServletRequest, ServletResponse) 这是重写的父类的服务方法,在这里会将ServletRequest和ServletResponse参数对象强制转换成http协议的HttpServletRequest和HttpServletResponse对象,转换完之后就会调用下面的service(HttpServletRequest, HttpServletResponse)方法。
service(HttpServletRequest, HttpServletResponse)
这是HttpServlet的服务方法,是具体到了http协议的服务方法,这个方法会被service(ServletRequest, ServletResponse)调用,这个方法的HttpServletRequest和HttpServletResponse参数对象就是由这个不带协议的service方法传递过来的。
do...系列方法,这个系列的方法会被带有http协议的service方法调用,它们的参数对象也是由这个service方法传递进来的:
doGet(HttpServletRequest, HttpServletResponse)
当浏览器是通过地址栏的URL或者超链接等连接的方式进行访问的,就会默认调用doGet方法,在表单中的提交访问则是可以选择性的调用。
doPost(HttpServletRequest, HttpServletResponse)
当浏览器是通过表单提交来进行访问的,可以选择调用doPost方法。
以下的五个方法的调用,是需要在http请求头文本中定义的:
doHead(HttpServletRequest, HttpServletResponse)
doPut(HttpServletRequest, HttpServletResponse)
doDelete(HttpServletRequest, HttpServletResponse)
doOptions(HttpServletRequest, HttpServletResponse)
doTrace(HttpServletRequest, HttpServletResponse)
从这几个方法的特性可以知道HttpServlet会调用哪个do系列的方法,是由浏览器中定义的请求方式决定的,我们可以们查看service(HttpServletRequest, HttpServletResponse)方法的源码,看看do系列方法是怎么被调用的:
从观察Servlet这几个类的派生体系,可以知道如果想要接收任何类型的浏览器请求,只需要重写service方法即可。
下面使用一个流程图来看一下Servlet的访问流程:
Servlet家族思维导图: