Spring注解驱动开发第51讲——ServletContainerInitializer来了,傻孩子们,快跑啊!

阅读: 评论:0

Spring注解驱动开发第51讲——ServletContainerInitializer来了,傻孩子们,快跑啊!

Spring注解驱动开发第51讲——ServletContainerInitializer来了,傻孩子们,快跑啊!

写在前面

在前一讲中,我们也大概知道了Servlet 3.0规范里面的一些简单注解,利用它们可以来注册Servlet、Filter以及Listener等组件。但是,这些注解不是我们要讲述的重点,因为毕竟原生的Servlet开发场景用到的还是比较少的。

那么,在这一讲中,我们来讲述什么呢?打开Servlet 3.0标准规范文档,找到Annotations and pluggability这一章节下的8.2 Pluggability这一小节,找到之后,再找到该小节下的最后一个小节,即Shared libraries / runtimes pluggability,翻译过来,应该是共享库/运行时插件能力

这是一个非常重要的机制,该机制在后来我们框架整合里面用到的非常多,所以,这一讲我们就来讲下它。

Shared libraries(共享库)/ runtimes pluggability(运行时插件能力)

我们好好看看Servlet 3.0标准规范文档中Shared libraries / runtimes pluggability这一小节,大概在该小节的第二段描述中,有句话说的是,container(即Servlet容器,比如Tomcat服务器之类的)在启动我们的应用的时候,它会来扫描jar包里面的ServletContainerInitializer的实现类

哦豁,我们现在知道了,当Servlet容器启动我们的应用时,它会扫描我们当前应用中每一个jar里面的ServletContainerInitializer的实现类。那究竟是一个怎么扫描法呢?我们再好好看看该小节的第二段描述,它说,我们得提供ServletContainerInitializer的一个实现类,提供完这个实现类之后还不行,我们还必须得把它绑定在META-INF/services/目录下面的名字叫javax.servlet.ServletContainerInitializer的文件里面。

也就是说,必须将提供的实现类绑定在META-INF/services/javax.servlet.ServletContainerInitializer文件中,所谓的绑定就是在javax.servlet.ServletContainerInitializer文件里面写上ServletContainerInitializer实现类的全类名,也就是说,javax.servlet.ServletContainerInitializer文件中的内容就是咱们提供的ServletContainerInitializer实现类的全类名。

至此,我们才总算搞清楚了这个非常重要的机制,总结一下就是,Servlet容器在启动应用的时候,会扫描当前应用每一个jar包里面的META-INF/services/javax.servlet.ServletContainerInitializer文件中指定的实现类,然后,再运行该实现类中的方法

接下来,我们就来测试一下该机制。

首先,我们来编写一个类,例如MyServletContainerInitializer,来实现ServletContainerInitializer接口。

imeixia.servlet;import java.util.Set;import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;public class MyServletContainerInitializer implements ServletContainerInitializer {@Overridepublic void onStartup(Set<Class<?>> arg0, ServletContext arg1) throws ServletException {// TODO Auto-generated method stub}}

然后,按照Servlet 3.0标准规范文档中所说的,将以上类的全类名配置在META-INF/services目录下的javax.servlet.ServletContainerInitializer文件中。一开始甚至连META-INF/services目录都没有,更何谈什么javax.servlet.ServletContainerInitializer文件,因此,我们得在当前项目的 类路径(即src目录) 下把META-INF/services这个目录给创建出来,接着在该目录下创建一个名字为javax.servlet.ServletContainerInitializer的文件。

这一切都弄完之后,我们就将咱们自己编写的MyServletContainerInitializer类的全类名配置在javax.servlet.ServletContainerInitializer文件,如下图所示。

这样的话,Servlet容器在我们的应用一启动的时候,就会找到以上这个实现类,并来运行它其中的方法。

那么运行该实现类的什么方法呢?我们发现MyServletContainerInitializer实现类中就只有一个叫onStartup的方法,因此Servlet容器在我们的应用一启动的时候,就会运行该实现类中的onStartup方法。

而且,我们还可以看到该方法里面有两个参数,其中一个参数是ServletContext对象,我们对它已经很熟悉了,它就是用来代表当前web应用的,一个web应用就对应着一个ServletContext对象。此外,它也是我们常说的四大域对象之一,我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到。

说完其中一个参数,我们着重来说第二个参数,即Set<Class<?>> arg0,它又是什么呢?我可以参照Servlet 3.0标准规范文档中的下面第三段描述,描述说,我们可以在ServletContainerInitializer的实现类上使用一个@HandlesTypes注解,而且在该注解里面我们可以写上一个类型数组哟,也就是说可以指定各种类型。

那么,@HandlesTypes注解有什么作用呢?Servlet容器在启动应用的时候,会将@HandlesTypes注解里面指定的类型下面的子类,包括实现类或者子接口等,全部给我们传递过来。

实践是检验真理的唯一标准,我们用案例说话。我们不妨先写一个我们感兴趣的类型,比如一个接口,名字可以叫HelloService,如下所示。

imeixia.service;public interface HelloService {}

旋即,我们就可以在咱们自己编写的MyServletContainerInitializer实现类上写上这样一个@HandlesTypes(value={HelloService.class})注解了。

imeixia.servlet;import java.util.Set;import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;imeixia.service.HelloService;@HandlesTypes(value={HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {/** 参数:*    ServletContext arg1:代表当前web应用。一个web应用就对应着一个ServletContext对象,此外,它也是我们常说的四大域对象之一,*    我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到*    *    Set<Class<?>> arg0:我们感兴趣的类型的所有后代类型*    */@Overridepublic void onStartup(Set<Class<?>> arg0, ServletContext arg1) throws ServletException {// TODO Auto-generated method stub}}

只要在@HandlesTypes注解里面指定上我们感兴趣的类型,那么Servlet容器在启动的时候就会自动地将该类型(即HelloService接口)下面的子类,包括实现类或者子接口等全部都传递过来,很显然,参数Set<Class<?>> arg0指的就是我们感兴趣的类型的所有后代类型。

接着,我们就为以上HelloService接口来写上几个实现。比如,先来写一个该接口的子接口,就叫HelloServiceExt,如下所示。

imeixia.service;public interface HelloServiceExt extends HelloService {}

再来创建一个实现该接口的抽象类,可以叫AbstractHelloService,如下所示。

imeixia.service;public abstract class AbstractHelloService implements HelloService {}

再再来创建一个该接口的实现类,例如HelloServiceImpl,如下所示。

imeixia.service;public class HelloServiceImpl implements HelloService {}

现在,HelloService接口下面有以上这三种不同的后代类型了。如此一来,Servlet容器在一启动的时候,就会把我们感兴趣的所有类型能传递过来,这时,我们就可以来输出一下了。

imeixia.servlet;import java.util.Set;import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;imeixia.service.HelloService;@HandlesTypes(value={HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {/** 参数:*    ServletContext arg1:代表当前web应用。一个web应用就对应着一个ServletContext对象,此外,它也是我们常说的四大域对象之一,*    我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到*    *    Set<Class<?>> arg0:我们感兴趣的类型的所有后代类型*    */@Overridepublic void onStartup(Set<Class<?>> arg0, ServletContext arg1) throws ServletException {// TODO Auto-generated method stubSystem.out.println("我们感兴趣的所有类型:");// 好,我们把这些类型来遍历一下for (Class<?> clz : arg0) {System.out.println(clz);}}}

可以看到,目前,我们暂时还用不到ServletContext对象参数。

最后,我们来启动项目,看一看Eclipse控制台会不会打印我们感兴趣的所有类型,如下图所示,确实是打印出了我们感兴趣的所有类型。

而且,还可以看到我们感兴趣的类型本身(即HelloService接口)没有打印之外,它下面的所有后代类型,不管是抽象类,还是子接口,还是实现类,都给打印出来了。

这也验证了这一点,即Servlet容器在启动应用的时候,会将@HandlesTypes注解里面指定的类型下面的子类,包括实现类或者子接口等,全部都给我们传递过来。那这样有啥子用呢?只要给我们传入了某一感兴趣的类型,我们是不是就可以利用反射来创建对象了啊!

以上就是基于运行时插件的ServletContainerInitializer机制。这个机制最重要的就是要启动ServletContainerInitializer的实现类,然后就能传入我们感兴趣的类型了,该机制有两个核心,一个是ServletContainerInitializer,一个是@HandlesTypes注解。

该机制我们就介绍到这,等到我们整合Spring MVC的时候,我们就会用到它了。

本文发布于:2024-02-04 14:31:42,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170709399256388.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:来了   孩子们   注解   快跑   Spring
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23