Spring MVC 解读——

转自:http://my.oschina.net/HeliosFly/blog/203149 做者:GoodLoser.

Spring MVC 解读---<context:component-scan/>

    注解是骑士魂牵梦绕的美丽公主,也是骑士的没法摆脱的噩梦...java

 

1、<context:component-scan/>

    想必@Component,@Repository,@Service,@Controller几个经常使用的Type-Level的Spring MVC注解,你们都很清楚他们的意思跟用途。标记为@Component的类,在使用注解配置的状况下,系统启动时会被自动扫描,并添加到bean工厂中去(省去了配置文件中写bean定义了),另外三个分别表示MVC三层模式中不一样层中的组件,他们都是被@Component标记的,因此也会被自动扫描。ios

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Target ({ElementType.TYPE})
@Retention (RetentionPolicy.RUNTIME)
@Documented
@Component //这里。。。
public  @interface  Repository {
     String value()  default  "" ;
}
 
@Target ({ElementType.TYPE})
@Retention (RetentionPolicy.RUNTIME)
@Documented
@Component //这里。。。
public  @interface  Service {
     String value()  default  "" ;
}
 
@Target ({ElementType.TYPE})
@Retention (RetentionPolicy.RUNTIME)
@Documented
@Component //这里。。。
public  @interface  Controller {
     String value()  default  "" ;
}

 

    为了达到以上效果,咱们还需在xml配置文件中加入以下定义web

?
1
<context:component-scan base- package = "com.springrock..." />

 

    这样Spring就能够正确的处理咱们定义好的组件了,重要的是这些都是自动的,你甚至不知道他是怎么作的,作了什么?若是不了解反射,可能真的感到吃惊了,但即使如此,我也想知道它到底作了什么?何时作的?spring

2、BeanDefinitionParser

    通过仔细的源码阅读,我找到了这个接口BeanDefinitionParser,文档描述说,它是一个用来处理自定义,顶级(<beans/>的直接儿子标签)标签的接口抽象。能够实现它来将自定义的标签转化为 BeanDefinition类。下面是它的接口定义express

?
1
BeanDefinition parse(Element element, ParserContext parserContext);

 

    其中Element是Dom api 中的元素,ParserContext则是用来注册转换来的bean 工厂。api

    或许你开始恼火说这么多跟上面有什么关系,好吧,下面即是我真正要说的,咱们来看下它有哪些实现类:mvc

    看到了吧,ComponentScanBeanDefinitionParser,正是咱们想要的,他就是用来将<context:component-scan/>标签转化为bean 的解析类。那他作了什么呢?app

?
1
2
3
4
5
6
7
8
9
10
11
12
public  BeanDefinition parse(Element element, ParserContext parserContext) {
         String[] basePackages = StringUtils.tokenizeToStringArray(
                                             element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
                 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
 
         // Actually scan for bean definitions and register them.
         ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
         Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
         registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
 
         return  null ;
     }

 

   很明显他会得到<component-scan/>的base-package属性,而后解析所需解析的包路径,而后他会建立一个ClassPathBeanDefinitionScanner对象,并委托它来执行对路径下文件的扫描,而后将得到的BeanDefinitions注册到bean工厂中。是否是很清晰?ide

    我想你会急切的知道ClassPathBeanDefinitionScanner 作了什么?post

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected  Set<BeanDefinitionHolder> doScan(String... basePackages) {
         Set<BeanDefinitionHolder> beanDefinitions =  new  LinkedHashSet<BeanDefinitionHolder>();
         for  (String basePackage : basePackages) {
             //这里是重点,找到候选组件
             Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
             for  (BeanDefinition candidate : candidates) {
                 //.....
                 //.....
                 if  (checkCandidate(beanName, candidate)) {
                     BeanDefinitionHolder definitionHolder = 
                                             new  BeanDefinitionHolder(candidate, beanName);
                     beanDefinitions.add(definitionHolder);
                     //注册到工厂中
                     registerBeanDefinition(definitionHolder,  this .registry);
                 }
             }                        
         }
         return  beanDefinitions;
     }

 

    重点是继承自父类ClassPathScanningCandidateComponentProvider 的findCandidateComponents方法,意思就是找到候选组件,而后注册到工厂中,那么它是怎么找到候选组件的呢?

咱们再看看

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public  Set<BeanDefinition> findCandidateComponents(String basePackage) {
         Set<BeanDefinition> candidates =  new  LinkedHashSet<BeanDefinition>();
         try  {
             String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                     resolveBasePackage(basePackage) +  "/"  this .resourcePattern;
             Resource[] resources =  this .resourcePatternResolver.getResources(packageSearchPath);
             for  (Resource resource : resources) {
                 if  (resource.isReadable()) {
                     try  {
                         MetadataReader metadataReader =  this .metadataReaderFactory.
                                                                 getMetadataReader(resource);
                         if  (isCandidateComponent(metadataReader)) {
                             ScannedGenericBeanDefinition sbd = 
                                               new  ScannedGenericBeanDefinition(metadataReader);
                             if  (isCandidateComponent(sbd)){
                                 candidates.add(sbd);
                             }
                         }
                     }
                 }
             }
         }
         return  candidates;
     }

 

   首先获取路径下的资源Resource,而后判断资源是否可读,而且获取可读资源的MetadataReader对象,而后再调用isCandidateComponent(MetadataReader)判段是不是候选组件,若是是,则生成该metadataReader的ScannedGenericBeanDefinition对象。最后判断ScannedGenericBeanDefinition是否为候选的,若是是则添加到工厂中。

3、includeFilters,excludeFilters

    能够看到经历了两次筛选,才找到最终的候选Bean,咱们来看第一个过滤作了什么?

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected  boolean  isCandidateComponent(MetadataReader metadataReader)  throws  IOException {
         for  (TypeFilter tf :  this .excludeFilters) { //excludeFilters 是什么?
             if  (tf.match(metadataReader,  this .metadataReaderFactory)) {
                 return  false ;
             }
         }
         for  (TypeFilter tf :  this .includeFilters) { //includeFilters 是什么?
             if  (tf.match(metadataReader,  this .metadataReaderFactory)) {
                 AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
                 if  (!metadata.isAnnotated(Profile. class .getName())) {
                     return  true ;
                 }
                 AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile. class );
                 return  this .environment.acceptsProfiles(profile.getStringArray( "value" ));
             }
         }
         return  false ;
     }

 

    咱们看到这里有两个实例变量excludeFilters, includeFilters,而后用他们两个去匹配传递进来的MetadataReader,若是与excludeFilter匹配成功返回false, 与includeFilter匹配成功返回true。那么这两个filter分别是什么呢?咱们打上断点,调试运行发现

    默认状况下includeFilters是一个含有两个值得List,分别是@Component注解和@ManageBean注解,而excludeFilter是个空List,好吧,如今豁然开朗了吧,原来就是它来筛选咱们的@Component标记的类。固然咱们能够自定义这两个filters,只需在<context:component-scan/>标签下加两个子标签便可, 像这样:

?
1
2
3
4
<context:component-scan base- package = "com.springrock" >
        <context:exclude-filter type= "annotation"  expression= "org.springframework.stereotype.Repository" />
        <context:include-filter type= "annotation"  expression= "com.springrock.whatever.youcustomized.annotation" />
</context:component-scan>

 

4、BeanDefinitionRegistry

    上面代码中咱们看到还有一个isCandidateComponent方法,它主要是判断当前类是不是具体的,而非抽象类和接口,以及是不是能够独立建立的没有依赖的?鉴于与咱们目前讨论的主题不相关,因此略去,感兴趣的话,能够本身查看下源码。

    好了,咱们既然知道了Spring是怎样经过<context:component-scan/>来扫描,过滤咱们的组件了,可是他是怎样将咱们定义的组件收集起来供后面的请求处理呢?

    咱们来看下上面doScan方法中有

?
1
2
//注册到工厂中
registerBeanDefinition(definitionHolder,  this .registry);

 

    这样一行代码,很明显是将beanDefinition注册到,registry中了。那这个registry是什么呢?是一个BeanDefinitionRegistry,下面是它的接口定义及继承结构:

?
1
2
3
4
5
6
7
8
9
10
public  interface  BeanDefinitionRegistry  extends  AliasRegistry {
     void  registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
             throws  BeanDefinitionStoreException;
     void  removeBeanDefinition(String beanName)  throws  NoSuchBeanDefinitionException;
     BeanDefinition getBeanDefinition(String beanName)  throws  NoSuchBeanDefinitionException;
     boolean  containsBeanDefinition(String beanName);
     String[] getBeanDefinitionNames();
     int  getBeanDefinitionCount();
     boolean  isBeanNameInUse(String beanName);
}

 

    咱们能够看到接口中定义了诸多beandefinition的注册,删除,获取等方法,而且Spring为咱们提供了三个内部实现,那么运行时,使用了那个实现呢?DefaultListableBeanFactory,是的就是它。它就是SpringMVC 中管理Bean的工厂了,咱们来看下,它的registerBeanDefinition是怎样实现的?

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  void  registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
             throws  BeanDefinitionStoreException {
         synchronized  ( this .beanDefinitionMap) {
             Object oldBeanDefinition =  this .beanDefinitionMap.get(beanName);
             if  (oldBeanDefinition !=  null ) {
                 if  (! this .allowBeanDefinitionOverriding) {
                     throw  new  BeanDefinitionStoreException();
                 }
                 else  {
                     if  ( this .logger.isInfoEnabled()) {
                         this .logger.info( "Overriding bean definition '"  + beanName +  "]" );
                     }
                 }
             }
             else  {
                 this .beanDefinitionNames.add(beanName);
                 this .frozenBeanDefinitionNames =  null ;
             }
             this .beanDefinitionMap.put(beanName, beanDefinition); //添加到beanDefinitionMap中了。
         }
         resetBeanDefinition(beanName);
     }

 

    从上面的代码能够看出,全部的beanDefinition都由实例变量beanDefinitionMap来保存管理,他是一个ConcurrentHashMap,beanName做为键,beanDefinition对象做为值。到这咱们知道了咱们的bean是怎样被注册管理的了。可是问题又来了,咱们的系统是在何时读取<context:component-scan/>标签,而且扫描咱们的bean组件的呢?

固然是从ContextLoaderListener开始了入手分析了。

5、ContextLoader

    咱们查看源码(篇幅问题,不贴代码了,很简答)发现ContextLoaderListener将web application context的初始化动做委托给了ContextLoader了,那ContextLoader作了什么呢?

?
1
2
3
4
5
6
7
if  ( this .context ==  null ) {
      this .context = createWebApplicationContext(servletContext);
}
if  ( this .context  instanceof  ConfigurableWebApplicationContext) {
      configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext) this .context,
                                              servletContext);
}

 

    上面的代码片断即是ContextLoader中initWebApplicationContext方法中的关键一段。首先会建立一个WebApplicationContext对象,而后configure 而且refresh这个WebApplicactionContext对象,是否是在这个configureAndRefreshWebApplicationContext方法中进行了配置文件的加载和组件的扫描呢?必须是啊。。。

?
1
wac.refresh();

 

    方法的最后有一个调用了wac的refresh方法,这个wac呢就是前面建立的WebApplicationContext对象,也就是咱们这个Web应用的上下文对象。具体是什么呢?咱们看一下createWebapplicationContext方法

?
1
2
3
4
5
6
protected  WebApplicationContext createWebApplicationContext(ServletContext sc) {
         Class<?> contextClass = determineContextClass(sc); //这里是关键
         ConfigurableWebApplicationContext wac =
                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
         return  wac;
     }

 

   这个方法先肯定咱们context的类型,调用了determineContextClass方法,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected  Class<?> determineContextClass(ServletContext servletContext) {
         //public static final String CONTEXT_CLASS_PARAM = "contextClass";
         String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
         if  (contextClassName !=  null ) {
             try  {
                 return  ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
             }
         }
         else  { //defaultStrategies 是关键
             contextClassName = defaultStrategies.getProperty(WebApplicationContext. class .getName());
             try  {
                 return  ClassUtils.forName(contextClassName, ContextLoader. class .getClassLoader());
             }
         }
     }

 

   这个方法先判断咱们servletContext中有没有contextClass这个初始化属性(在web.xml的init-param标签中配置),一般咱们不会配置这个属性。那确定是null了,因此它接着去查看defaultStrategy中有没有相应属性,那这个defaultStrategy是什么呢?下面是ContextLoader中一个静态代码块,也就说只要ContextLoader被加载,defaultStrategy便会被赋值。

?
1
2
3
4
5
6
7
8
static  {
         try  {
             //private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
             ClassPathResource resource =  new  ClassPathResource(DEFAULT_STRATEGIES_PATH,
                                              ContextLoader. class );
             defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
         }
     }

 

    很明显,系统是去ClassPath下读取一个Context.properties的属性文件,并赋值给defaultStrategy,这个属性文件以下:

?
1
2
org.springframework.web.context.WebApplicationContext
                               =org.springframework.web.context.support.XmlWebApplicationContext

 

   啊哈,终于找到了,原来是XmlWebApplicationContext啊,这就是咱们的WebApplicationContext具体实现对象。

既然找到他了,那咱们看看他的refresh()方法作了什么呢?

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  void  refresh()  throws  BeansException, IllegalStateException {
         synchronized  ( this .startupShutdownMonitor) {
             prepareRefresh();
             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
             prepareBeanFactory(beanFactory);
             try  {
                 postProcessBeanFactory(beanFactory);
                 invokeBeanFactoryPostProcessors(beanFactory);
                 registerBeanPostProcessors(beanFactory);
                 initMessageSource();
                 initApplicationEventMulticaster();
                 onRefresh();
                 registerListeners();
                 finishBeanFactoryInitialization(beanFactory);
                 finishRefresh();
             }
         }
     }

 

5、Bean Factory   

这么多代码中,只有第二行与咱们当前讨论的主题有关,这一行会尝试获取一个新鲜的BeanFactory,这个BeanFactory与咱们以前说的那个BeanDefinitionRegistry有什么关系呢?继续看代码:

?
1
2
3
4
5
protected  ConfigurableListableBeanFactory obtainFreshBeanFactory() {
         refreshBeanFactory();
         ConfigurableListableBeanFactory beanFactory = getBeanFactory();
         return  beanFactory;
     }

 

   在getBeanFactory以前,先进行了一个refreshBeanFactory的操做来刷新当前的BeanFactory,咱们以此来看一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
     protected  final  void  refreshBeanFactory()  throws  BeansException {
         if  (hasBeanFactory()) {
             destroyBeans();
             closeBeanFactory();
         }
         try  {
             DefaultListableBeanFactory beanFactory = createBeanFactory();
             beanFactory.setSerializationId(getId());
             customizeBeanFactory(beanFactory);
             loadBeanDefinitions(beanFactory);
             synchronized  ( this .beanFactoryMonitor) {
                 this .beanFactory = beanFactory;
             }
         }
     }

 

    代码依旧很清晰,先判断有没有BeanFactory,若是有,销毁全部Bean,关闭BeanFactory,而后从新建立一个BeanFactory,并将其赋给beanFactory实例变量,有没有发现这个beanFactory是个DefaultListableBeanFactory啊?咱们上边讲到的bean definition registry也是个DefaultListableBeanFactory记得吗?他们会不会是同一个呢?答案是yes。重点就在这个loadBeanDefinition(beanFactory)方法上了,很明显:加载Bean Definition到bean工厂中,是否是与咱们上边讲到的对上了?

loadBeanDefinition中,Spring会读取xml配置文件,而后会读取里面的bean定义,这一切都是委托给了文章开头的BeanDefinitionParser来完成的,能够看到除了<context:component-scan/>的Parser,还有<mvc:annotation-driven/>的parser,还有<interceptors/>的parser等。是否是比较清晰了?

    固然,咱们的问题及好奇心远不止这些,这篇文章只是讲解了其中的一小个:系统的初始化作了什么,在何时加载咱们定义的beans,咱们定义的bean被放到了哪里? 等等,如今问题又来了,咱们怎样使用咱们的bean呢?或者说若是被标记为@Autowire的属性,是怎样被自动装配的呢?@RequestMapping怎样工做的呢?Spring怎样正确调用controller来处理请求呢?等等,后面的文章咱们一一解答。

相关文章
相关标签/搜索