context:annotation-config和context:component-scan

一、XML配置方式

         现在有三个类ClassA、ClassB和ClassC,ClassA和ClassB、ClassC不在同一个包下,其中ClassA中有两个引用属性classB和classC,它们分别是ClassB和ClassC的实例。

package com.codeproject.jackie.springdive.beanconfig.yyy;
public class ClassB {
  public ClassB() {
    System.out.println("creating instance of ClassB :" + this);
  }
}

package com.codeproject.jackie.springdive.beanconfig.yyy;
public class ClassC {
  public ClassC() {
    System.out.println("creating instance of ClassC :" + this);
  }
}

package com.codeproject.jackie.springdive.beanconfig.xxx;
import com.codeproject.jackie.springdive.beanconfig.yyy.ClassB;
import com.codeproject.jackie.springdive.beanconfig.yyy.ClassC;
public class ClassA {
  private ClassB classB;
  private ClassC classC;
  public ClassA() {
    System.out.println("creating instance of ClassA :" + this);
  }
  public void setClassB(ClassB classB) {
    System.out.println("setting field classB with " + classB + " in ClassA");
    this.classB = classB;
  }
  public void setClassC(ClassC classC) {
    System.out.println("setting field classC with " + classC + " in ClassA");
    this.classC = classC;
  }
}

    下面是相应的XML配置:   

<bean id="classB" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassB" />
<bean id="classC" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassC" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA">
  <property name="classB" ref="classB" />
  <property name="classC" ref="classC" />
</bean>

         加载Spring容器后输出以下结果:

creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@10d09ad3
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@4178460d
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@3f3f210f
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@10d09ad3 in ClassA
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@4178460d in ClassA

二、注解配置方式

         使用注解就是为了简化XML配置,下面对上面的例子做些改动。在ClassA中的setter方法上增加@Autowired注解。

       package com.codeproject.jackie.springdive.beanconfig.xxx;
       import com.codeproject.jackie.springdive.beanconfig.yyy.ClassB;
       import com.codeproject.jackie.springdive.beanconfig.yyy.ClassC;
       import org.springframework.beans.factory.annotation.Autowired;
       public class ClassA {
           private ClassB classB;
           private ClassC classC;
           public ClassA() {
               System.out.println("creating instance of ClassA :" + this);
           }
           @Autowired
           public void setClassB(ClassB classB) {
               System.out.println("setting field classB with " + classB + " in ClassA");
               this.classB = classB;
           }
           @Autowired
           public void setClassC(ClassC classC) {
               System.out.println("setting field classC with " + classC + " in ClassA");
               this.classC = classC;
           }
       }
          XML配置文件修改如下:   
<bean id="classB" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassB" />
<bean id="classC" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassC" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
           再次加载Spring容器,输出以下结果:
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@48082d37
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@10aadc97
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@4178460d

          从结果中我们可以发现属性classB和classC没有自动注入到ClassA中。

        这是因为我们还缺少能够找到并处理这些注解的工具---注解处理器,注解本身并不能够做任何其它事情,它们只是用来注释一些东西。

三、<context:annotation-config>

         为了让Spring能够自动装配属性classB和classC以解决上面的问题,可以在XML文件注册AutowiredAnnotationBeanPostProcessor这个Bean后处理器。

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
            通常我们会采用一种更优雅的方式---即使用 <context:annotation-config>。

        <context:annotation-config>会隐式地向Spring容器中AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor以及RequiredAnnotationBeanPostProcessor这四个Bean后处理器。注册这四个Bean后处理器的作用是为了在系统中识别并激活相应的注解,@Autowired、@Resource @PostConstruct@PreDestroy、@PersistenceContext以及@Required等注解

        现在将XML配置文件修改如下:

<context:annotation-config />
<bean id="classB" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassB" />
<bean id="classC" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassC" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
           再次加载Spring容器,输出结果如下:
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@236527f
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@59556d12 
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@1a0fced4 
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@236527f in ClassA 
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@59556d12 in ClassA
          输出的确是我们想要的结果。但是XML配置文件并没有简化多少。下面我删掉XML中的bean声明并使用用 @Component 注解来替换它们。      
       package com.codeproject.jackie.springdive.beanconfig.yyy;
       import org.springframework.stereotype.Component;
       @Component
       public class ClassB {
           public ClassB() {
               System.out.println("creating instance of ClassB :" + this);
           }
       }

       package com.codeproject.jackie.springdive.beanconfig.yyy;
       import org.springframework.stereotype.Component;

       @Component
       public class ClassC {
           public ClassC() {
               System.out.println("creating instance of ClassC :" + this);
           }
       }

       package com.codeproject.jackie.springdive.beanconfig.xxx;
       import com.codeproject.jackie.springdive.beanconfig.yyy.ClassB;
       import com.codeproject.jackie.springdive.beanconfig.yyy.ClassC;
       import org.springframework.beans.factory.annotation.Autowired;
       import org.springframework.stereotype.Component;

       @Component
       public class ClassA {
           private ClassB classB;
           private ClassC classC;
           public ClassA() {
               System.out.println("creating instance of ClassA :" + this);
           }
           @Autowired
           public void setClassB(ClassB classB) {
               System.out.println("setting field classB with " + classB + " in ClassA");
               this.classB = classB;
           }
           @Autowired
           public void setClassC(ClassC classC) {
               System.out.println("setting field classC with " + classC + " in ClassA");
               this.classC = classC;
           }
       }
    
而XML文件中只保留下面这个:
<context:annotation-config />
         加载Spring容器后发现什么都没有输出,没有创建bean,也没有注入bean。为什么呢?
       这是因为 <context:annotation-config /> 仅仅作用于在Spring容器中注册过的bean。由于我删掉了XML文件中那三个bean的定义所以没有bean被创建,<context:annotation-config /> 也就没有了作用目标。
 

四、<context:component-scan>

        Spring通过<context:component-scan>提供了强大的组件扫描功能,在这个XML元素中必须指定base-package这个属性,其值即为要扫描的包,Spring会自动扫描指定的包和子包下所有使用了@Component、@Repository、@Service以及@Controller注解的类,将它们注册为Spring bean。其中@Component注解是基本注解,@Repository、@Service和@Controller分别表示持久层、服务层和表现层中的组件

       下面使用<context:component-scan>来简化XML配置:

<context:component-scan base-package="com.codeproject.jackie.springdive.beanconfig" />
           再次 加载Spring容器,  得到正确的输出结果:
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@1fbbd7b2 
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@34d507e9 
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@34d507e9 in ClassA 
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@214a7a12 
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@214a7a12 in ClassA
           现在将XML配置文件修改如下: 
<context:component-scan base-package="com.codeproject.jackie.springdive.beanconfig.yyy" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
           加载Spring容器,也可以得到正确的结果:
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@bb273cc 
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@45660d6 
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@3288df60 
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@bb273cc in ClassA 
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@45660d6 in ClassA
           可见,即使id为classA的这个bean不是通过包扫描而是在XML文件中手工注册的, 注解处理器仍被<context:component-scan>应用于所有在Spring容器中注册的bean上,亦即<context:component-scan>有着和<context:annotation-config />相同的功能

       那如果在XML配置文件中同时指定了<context:annotation-config/>和 <context:component-scan>呢? 

<context:annotation-config />
<context:component-scan base-package="com.codeproject.jackie.springdive.beanconfig.yyy" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
      没有重复注册id为classA的这个bean,再一次获得了期望的结果:
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@bb273cc
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@45660d6
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@3288df60
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@bb273cc in ClassA
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@45660d6 in ClassA
          这是因为两个标记注册了相同的注解处理器 (如果指定了<context:component-scan>那么<context:annotation-config />就会被忽略) ,但是Spring只会运行它们一次。

       即使你自己注册注解处理器多次,Spring将仍然保证只执行一次。修改XML配置如下:

<context:annotation-config />
<context:component-scan base-package="com.codeproject.jackie.springdive.beanconfig.yyy" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
<bean id="bla" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla1" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla2" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla3" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
            我们仍然得到了正确的输出:   
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@70d05c13
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@42ef83d3
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@3801318b
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@70d05c13 in ClassA
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@42ef83d3 in ClassA

五、总结

       <context:annotation-config>是用来激活那些已经在Spring容器里注册过的bean(无论是通过xml还是包扫描定义的)上面的注解,而<context:component-scan>除了具有<context:annotation-config>的功能之外,还可以扫描package来查找并注册bean。

六、参考资料

         http://stackoverflow.com/questions/7414794/difference-between-contextannotation-config-vs-contextcomponent-scan



你可能感兴趣的:(spring)