暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Servlet实例化与Inject注入实现(InstanceManager)

中间件技术讨论圈 2016-08-09
535

Servlet的实例化在StandardWrapper中的loadServlet方法中进行创建的:


我们并没有在StandardWrapper中直接看到Class.forName的字样,而是委托一个实例管理器,InstanceManager来对Servlet实例进行创建的;

对于Servlet实例的创建,其不仅仅是反射一下就OK了,这样我们也没有单独拿一篇文章讲它的必要,我们关注的是其Servlet实例众多的@Inject是如何注入的,并且在其执行的时候,怎么进行查找;

本文带你进一步的深入研究Servlet的实例化和资源注入;


1.DefaultInstanceManager

首先,看看InstanceManager的类结构关系吧,分析下图:


自定义的Servlet 是被StandardWrapper包装出来的,所以二者对应关系是一对一;

每一个StandardContext持有该应用的servlet实例化构造器InstanceManager;

StandardWrapper在创建Servlet 的时候,首先去通过StandardContext获取该应用下面的InstanceManager,然后直接执行newInstance方法,进行自定义的Servlet 的反射和实例化;


对于StandardContext中的InstanceManager实例,默认是DefaultInstanceManager进行创建的:


需要注意的是,injectMap是该应用下所有的注入节点,如@Inject,@Ejb等各种资源的集合,我们可以查看buildInjectionMap方法是怎么构建出来的:


一共7种资源,通过namingResource(也就是Tomcat本身的JNDI系统)在JNDI系统中进行查找,如果有的话,将这个jndiName直接作为Map的value部分,存入到最后的injectionMap中;


2.Inject注入实现

到这里为止,我们获得了一个该应用全集injectionMap,并针对于请求访问的Servlet,初始化DefaultInstanceManager


总共分四个步骤:

步骤1:前面定义的应用全集的注入集合injectionMap,是基于所有应用的,而这里是基于特定的Servlet的,所以需要需要从injectionMap中get到自己的Servlet的inject集合,这个就是上面的assembleInjectionsFromClassHierarchy 了:


步骤2:在前面我们得到injectionMap集合,这个集合的value不是引用的本身,而是jndiName,之所以没有将这些引用直接实例化是因为,对于这些引用非常占内存,并且初始化的时间非常的长,我们是否想过一个好的办法,假设每一次都引用,是否将这些都缓存起来,第一次虽然费点劲,初始化时间很长,而下一次就直接可以跳过初始化这一步了呢?

这个步骤就是populateAnnotationCache的方法了:


Tomcat将每一个Annotation条目都做成了AnnotationCacheEntry了,这一步主要是将这些映射关系建立起来,并没有直接把引用创建出来,直接赋值到AnnotationCacheEntry中去,因为这个是第3步的事情

步骤3:processAnnotations,第三步就是将引用真是的存入AnnotationCacheEntry中去:


查询的方式,是直接通过tomcat自身的JNDI系统进行查询,如果是方法的话,再进行method.invoke,如果是field的话,直接返回field即可;

当这一步操作做完以后,AnnotationCacheEntry就缓存完毕了,下一次再请求的Servlet的话,Servlet的实例化不必再走过这么多的步骤了,特别是反射;

不过,我们在前面也得知,目前Servlet基本是单实例多线程的,也就是说基本上也没有几次创建Servlet的机会,这些主要还是用在SingleThreadMode的模式中,Servlet创建比较多的时候会提高很多效率;


步骤4:对于@PostConstruct的的方法的回调,这个是为了JAVA EE规范中的Common Annotation规范的,整体思路也是查询方法,然后进行回调注入的方法,这里就不再缀余了,没有什么新意,和前面思路差不多;


3.Servlet访问限制过滤

最后,值得一提的是,DefaultInstanceManager的初始化的时候,可以看到其读取三个tomcat配置文件中的内容


这些配置文件是可以对servlet,Listener,filter的访问起到限制作用,以RestrictedServlets.properties:


构造方法将这些读取出来,然后在checkAccess访问的时候,对其进行过滤,看是否能够访问,如果可以,继续,如果不行的话,直接return并抛出响应的异常:



总结:

DefaultInstanceManager是实际的Servlet实例化反射并注入的实现类,其提供了一个注入缓存思路可以在Servlet配置为SingleThreadMode的模式下,提高很多效率,并且该类还提供了针对于servlet,Listener,filter通过配置文件起到限制访问的作用!


文章转载自中间件技术讨论圈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论