<context:annotation-config /> 与 <context:component-scan /> 与 <mvc:annotation-driven /> 之间的联系与区别

AUTHOR | nicechi
类别 | Spring
发表 | 2019-09-17 08:05:37
更新 | 2020-08-05 21:28:41

<context:annotation-config />

<context:annotation-config /> 是一个用在 Spring 的 XML 配置文件中的一个标签,通过这个标签可以激活在当前的 Spring context 中已经被注册(即已经通过 <bean> ... </bean> 或者 @Bean 的方式注册)的 bean 的注解(比如说 @Autowired 注解或者 @Qualified 注解等)。

再次强调:它激活的是已经被注册的 bean,这点是与 <context:component-scan /> 最本质的区别

下面代码声明了两个类(Person 和 Country),并且想要通过 @Autowired 将 Country 自动注入到 Person 里

//本例子的结构
java
|--com
|  |--nice
|  |   |--demo
|  |   |  |--Person.java
|  |   |  、--Country.java
|  、--App.java(启动类)
、--bean.xml(Spring 的配置文件)
// Person
public class Person {

    private String name=“nicechi”;
    private Country country;


    //使用 @Autowired 注解自动注入 Country bean
    //此 @Autowired 需要通过 <context:annotation-config /> 或 <context:component-scan /> 激活
    @Autowired
    public void setCountry(Country country) {
        this.country = country;
    }

    ...
    other getters and setters 
    ...

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", country=" + country +
                '}';
    }
}

//Country
public class Country {

    private String countryName=“China”;
   
    ... 
    getter and setter
    ...

    @Override
    public String toString() {
        return "Country{" +
                "countryName='" + countryName + '\'' +
                '}';
    }
}

如果仅仅在 spring contex 里注册了 Person 和 Country 这两个 bean,但是没有使用 <context:annotation-config /> 的话, @Autowired 将不会起作用,也就是说 Country bean 将不会注入到 Person bean 中

<!--beans.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.nice.demo.Person" />

    <bean id="country" class="com.nice.demo.Country" />

</beans>
public class App 
{
    public static void main( String[] args )
    {
        ApplicationContext applicationContext=
                 new ClassPathXmlApplicationContext("beans.xml");
        Person person=(Person) applicationContext.getBean("person1");
        
        //output: Person{name='nicechi', country=null}
        //可见 Country bean 并没有注入到 Person 中 
        System.out.println(person.toString());
    }
}

如果此时在上面的 beans.xml 中添加上 <context:annotation-config /> 标签的话,@Autowired 将会生效,Country bean 将会注入到 Person bean 中 

<!--beans.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.nice.demo.Person" />

    <bean id="country" class="com.nice.demo.Country" />

    <context:annotation-config/>

</beans>
public class App 
{
    public static void main( String[] args )
    {
        ApplicationContext applicationContext=
                 new ClassPathXmlApplicationContext("beans.xml");
        Person person=(Person) applicationContext.getBean("person");
        
        //output: Person{name='nicechi', country=Country{countryName='China'}}
        //可见 Country bean 已经被注入到 Person 中 
        System.out.println(person.toString());
    }
}

<context:component-scan /> 

<context:component-scan /> 也是一个用在 Spring 的 XML 配置文件中的一个标签,通过这个标签可以将扫描到的 bean (自动将使用 @Component、@Controller、@Service、@Repository 注解的类注册为 bean )注册到 spring context 中,同时执行与 <context:annotation-config /> 同样的功能:激活在当前的 Spring context 中已经被注册的 bean 的注解。

所以说,可以认为 <context:component-scan /> 在逻辑上继承了 <context:annotation-config />,因为 <context:component-scan /> 所做的就是 <context:annotation-config /> 所做的事情,但是在它 <context:annotation-config /> 的功能的基础上还添加了自动注册 bean 的功能。此外 <context:component-scan /> 还提供了一个属性: annotation-config ,用来关闭或开启 annotation-config 的功能(即关闭或开启激活在当前的 Spring context 中已经被注册的 bean 的注解的功能)

如果此时将上一节例子中的 Person 和 Country 都添加上 @Compone 注解,预期的结果是 Person 和 Country 不用通过 <bean> ... </bean> 或者 @Bean 的方式进行显式的注册,而是会被自动注册到 Spring Context 中 

//添加 @Component 后的 Person
@Component
public class Person {

    private String name=“nicechi”;
    private Country country;


    //使用 @Autowired 注解自动注入 Country bean
    //此 @Autowired 需要通过 <context:annotation-config /> 或 <context:component-scan /> 激活
    @Autowired
    public void setCountry(Country country) {
        this.country = country;
    }

    ...
    other getters and setters 
    ...

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", country=" + country +
                '}';
    }
}

//添加 @Component 后的 Country
@Component
public class Country {

    private String countryName=“China”;
   
    ... 
    getter and setter
    ...

    @Override
    public String toString() {
        return "Country{" +
                "countryName='" + countryName + '\'' +
                '}';
    }
}

在没有在 XML 使用 <context:component-scan /> 的情况下,使用 @Component、@Controller、@Service、@Repository 的类将不会自动注册到 Spring Context 中

public class App 
{
    public static void main( String[] args )
    {
        ApplicationContext applicationContext=
                new ClassPathXmlApplicationContext("beans.xml");

        //output: No bean named 'person' available
        //可见,id 名为 person 的 bean 并没有存在 Spring Context 中,
        这也就意味 @Component 并没有生效
        Person person=(Person) applicationContext.getBean("person");
        System.out.println(person.toString());
    }
}

如果此时在 bean.xml 中添加上 <context:component-scan /> 的话,此时 @Component、@Controller、@Service、@Repository 的类才会自动注册到 Spring Context 中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.nice.demo"/>

</beans>
public class App
{
    public static void main( String[] args )
    {
        ApplicationContext applicationContext=
                new ClassPathXmlApplicationContext("beans.xml");
        Person person=(Person) applicationContext.getBean("person");
        //output: Person{name='nicechi', country=Country{countryName='China'}}
        //说明此时 @Component 已经生效了 
        System.out.println(person.toString());
    }
}

<mvc:annotation-driven />

<mvc:annotation-driven /> 也是一个用在 Spring 的 XML 配置文件中的一个标签,它不同于前面两个所提到的标签,这个标签专门用于 Spring MVC ,用来自动注册 Spring MVC 所需要使用的 HandlerMapping 和 HandlerAdapter(即 DefaultAnnotationHandlerMapping / AnnotationMethodHandlerAdapter 或者 RequestMappingHandlerMapping / RequestMappingHandlerAdapter)。关于 HandlerMapping 和 HandlerAdapter 的内容请移步:浅析不同的 HandlerMapping 以及 HandlerAdapter 之间的作用以及区别 

HandlerMapping 用来映射 Dispatcher Servlet 所捕获的 request 到相对应的 handler 上(即使用有 @RequestMapping 或者 @GetMapping 等的方法),而 HandlerAdapter 用来调用所映射到的 handler(因为 Dispatcher Servlet 无法执行相对应的 handler,而是需要通过 HandlerAdapter 来执行相对应的 handler)

HandlerMapping 和 HandlerAdapter 都是 interface,而 DefaultAnnotationHandlerMapping / RequestMappingHandlerMapping 和 AnnotationMethodHandlerAdapter / RequestMappingHandlerAdapter 分别是他们的实现类,DefaultAnnotationHandlerMapping 与 AnnotationMethodHandlerAdapter 在 Spring 3.1 之后已经被遗弃,取而代之的是 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter。

所以说,如果在 Spring 3.1 及之前使用 <mvc:annotation-driven /> 的话,自动注册的将是 DefaultAnnotationHandlerMapping 与 AnnotationMethodHandlerAdapter ,如果在 Spring 3.2 及之后使用 <mvc:annotation-driven /> 的话,自动注册的将是 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 。

三者之间的联系与区别

<context:annotation-config /> 和 <context:component-scan /> 都是用来激活 bena 中的注解(例如 @Autowired 和 @Qualified 等),但是 <context:annotation-config /> 用来激活已经存在 Spring Context 中的 bean(通过 <bean> ... </bean> 或者 @Bean 的方式来注册),而 <context:component-scan /> 用来自动注册使用有 @Component、@Controller、@Service、@Repository 注解的类,并同时激活这些类中的 注解,所以说 <context:component-scan /> 在逻辑上继承了 <context:annotation-config /> 的功能,并同时添加了自动注册的功能。

而 <mvc:annotation-driven /> 是用于 Spring MVC 的标签,用来注册 DefaultAnnotationHandlerMapping / AnnotationMethodHandlerAdapter  或者 RequestMappingHandlerMapping / RequestMappingHandlerAdapter。

最后,希望这篇文章能带给你点启发,Have Fun!


CATEGORY

TOP