在spring mvc中扫描注解机制是我们理解javabean是怎么被加载,是如何被spring进行管理的第一步。那spring mvc 是如何扫描所有的编译文件并对注解进行操作的呢,下面我们来看下:
在spring-mvc中我们都会配置一个web.xml文件,内容如下:
我们知道只要servlet中的load-on-startup配置了大于1的数字,类就会在应用启动的时候被加载,在加载时候是调用了DispatcherServlet 的init()方法。
但我们进入dispatcherServlet并没有找到init()方法,此时我们继续查找父类,在其父类的父类HttpServletBean这个类中找到了init()方法:
init方法中有一个initServletBean()方法,这个方法就是用来初始化javabean的。
在其子类,FrameworkServlet中我们看到了initServletBean方法的实现方法,其方法中有这样一行代码:
其实,只要获取到ApplicationContext就能获取到javabean信息,此时我们进去看下,其是如何初始化ApplicationContext的:
在FrameworkServlet中,标红代码创建了ApplicationContext,关于其中的parent参数,以后再说,继续跟进,在createWebApplicationContext方法中:
其实,不论是ClassPathXmlApplicationContext还是WEB装载都会调用这段方法,区别只是在于,web容器中,用servlet装载时,servlet包装了一个XmlWebApplicationContext而已,无论是ClassPathXmlApplicationContext还是XmlWebApplicationContext他们都继承自抽象类:AbstractApplicationContext,他们共用了AbstractApplicationContext的refresh()方法:
虽然这些一层调用一层看起来很烦,但还是希望大家有时间看下去,因为在spring-mvc装载bean的时候不仅要读取解析xml文件还要对class文件进行bean读取加载,并且在xml解析,类加载,实例化的过程中有很多需求,并且spring-mvc采用了分离设计,下面我们来看看obtainFreshBeanFactory()方法:
在obtainFreshBeanFactory()方法中,加载bean的是第一行refreshBeanFactory()方法,而不是getBeanFactory()方法。
关于refreshBeanFactory()的实现是在AbstractRefreshableApplicationContext类中实现了,它继承自AbstractApplicationContext同时,
ClassPathXmlApplicationContext与XmlWebApplicationContext也继承了AbstractRefreshableApplicationContext这个类
在第一行,他createBeanFactory,同时在loadBeanDefinitions(beanFactory)中加载bean并将bean放入了BeanFactory中,继续跟踪loadBeanDefinitions方法,它是由AbstractXmlApplicationContext类中的方法实现,web项目中会由XmlWebApplicationContext来实现,loadBeanDefinitions方法如下:
它通过XmlBeanDefinitionReader类去读取springXML中的信息(也就是解析SpringContext.xml)并加载bean,接下来会调用多层,并在XMLBeanDefinitionReader类中去调用doLoadBeanDefinitions、regiseterBeanDefinitions操作,在regiseterBeanDefinitions的时候就开始解析XML了。它调用了DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,如下图所示:
在这个方法中做了解析XML中的操作,看标红代码,这段代码做了bean的解析工作。跟踪进去会发现里面解析了XML的信息,其中NamespaceHanderSupport的parse方法会根据节点的类型,找到合适的解析(BeanDefinitionParse)方式,他们预先已经被注册号了,放在一个HashMap中,例如在Spring的annotation的注解扫描中,我们通常会配置:<context:component-scan base-package="com.xxx">,此时根据名称“component-scan”就会找到对应的解析器来解析,此时解析注解是用的ComponentScanBeanDefinitionParser的parse方法。
在parse获取到后,有一个关键的步骤就是定义了ClassPathBeanDefinitionScanner来扫描类信息来对class文件进行扫描:
这个方法中最重要的就是doScan方法的作用,那doScan方法是如何扫描的呢,这里可以跟踪进去一起看看:
我们先跟进去找到findCandidateComponents方法:
在此方法中通过标红代码获取路径,并取得了class文件,那么是怎么获取到class文件的呢,有兴趣的朋友可以将classpath路径写为:自己项目编译文件路径例如:classpath*:org/sinovate/**/*.class,并用如下操作:
在控制台输出resources信息便可以看到class文件路径名称信息等。在获取到.class路径以及文件名称后,我们就可以通过classLoaderListenser加载.class文件了,此时在根据Annotation类的方法就可以对@Controller,@Service,@Resource,等一系列注解进行控制与装载了。由于Annotation方法较为简单,有兴趣的可以去网上搜索一片了解一下,下面是个Annotation注解解析的简单示例连接,有兴趣的朋友可以看下:
Annotation注解及解析机制