在项目中,我们为了避免代码的冗余,往往会进行分层创建,例如实体类dao层,service层和Controller层,我们会在这些类上添加注解@Repository、@Service、@Controller、@Component注解。然后通过包扫描的方式,将这些类注入到Spring容器中。
在spring中同样是分为xml配置文件的方式实现包扫描,注解的方式实现包扫描。

xml配置文件的方式实现包扫描

同样在前面创建的工程中的bean.xml配置文件,在原有的基础上添加标签,添加base-package属性,指定扫描的包

  1. <!--添加包扫描,在包com.gaoxiaobai下的以及起子包下的文件都会被扫描,只要类上有注解@Repository、@Service @Controller @Component 中的一个,就会将该类注入到容器中
  2. -->
  3. <context:component-scan base-package="com.gaoxiaobai"></context:component-scan>

在com.gaoxiaobai目录下创建三个包,分别为 dao、service和controller三个包,在三个包中分别创建一个类,分别为:BookDao、BookService、BookController

  • BookDao ```java package com.gaoxiaobai.dao;

import org.springframework.stereotype.Repository;

/**

  • @Author:gaoxiaobai
  • @Date:2022/7/123:13 */ @Repository public class BookDao { } ```
  • BookService ```java package com.gaoxiaobai.service;

import org.springframework.stereotype.Service;

/**

  • @Author:gaoxiaobai
  • @Date:2022/7/123:13 */ @Service public class BookService { } ```
  • BookController ```java package com.gaoxiaobai.controller;

import org.springframework.stereotype.Controller;

/**

  • @Author:gaoxiaobai
  • @Date:2022/7/123:14 */ @Controller public class BookController { }
    然后,通过创建一个测试方法,将spring容器中的所有对象都打印输出,看看在容器中是不是具有我们添加注解的这三个类的对象。<br />测试方法:
    ```java
    @Test
    public void testComponentScanByXML(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    String [] beanDefinitionNames = ac.getBeanDefinitionNames();
    for (String name: beanDefinitionNames) {
        System.out.println(name);
    }
    }
    
    image.png
    在执行结果中,可以看到我们添加注解的三个类的对象,还有spring框架中的一些对象,以及之前添加的对象。

    通过注解的方式实现包扫描

    在前一节使用注解@Configuration在类上添加,实现配置类代替xml配置文件,前面xml添加包扫描是在xml配置文件中添加一行代码。那采用注解也同样是在配置类上添加注解@ComponentScan来完成包扫描的添加。在这个注解中通过属性value来指定要扫描的包路径。指定扫描的包为com.gaoxiaobai即可。
    测试代码如下:
    @Test
    public void testComponentScanByAnnotation(){
    ApplicationContext ac = new AnnotationConfigApplicationContext(mainConfig.class);
    String[] names = ac.getBeanDefinitionNames();
    for (String name:names) {
        System.out.println(name);
    }
    }
    
    image.png
    可以看出使用注解的方式,与使用xml配置文件的方式可以达到一样的效果,那么大家更喜欢哪种配置方式呢?我个人还是偏向于注解方式,当然,每个人的观点不同,适合自己的就是最好的。

    在注解ComponentScan中有两个方法,可以指定只扫描某种类型的注解,或者排除不注入某种类型的注解。

    通过这两个方法可以实现更小范围的对象注入到容器中。进入注解ComponentScan的内部,可以发现有两个方法:
    image.png

    扫描时排除注解标注的类

    比如现在有一种需求,排除注解@Controller 和@Service标注的类,其他类型的注解标注的类都注入IOC容器中,可以通过在注解@ComponentScan上添加方法excludeFilters(),添加方式如下:
    @ComponentScan(value = "com.gaoxiaobai",excludeFilters = {
      //通过type属性指定按照什么规则进行排除:按注解进行排除,按给定的类型排除,按正则表达式排除
     //classes:给出要排除对象的class集合
         @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={Controller.class, Service.class})
    })
    
    测试结果:
    image.png
    在结果中没有了之前打印输出的BookService和BookController两个对象,说明@Controller和@Service两类注解的类没有被注入到Spring容器中。

    扫描时只添加指定的注解标注的类

    现在想要只包含@Controller注解标注的类,其他的注解标注的类不被注入到Spring容器中。这里需要与排除时多做一个配置,因为ComponentScan的默认扫描方式是全部添加,当需要在扫描时只添加指定的注解的类,那么需要将原来默认的扫描添加方式置为false,使得自定义的扫描方式可以生效。
    @ComponentScan(value = "com.gaoxiaobai",includeFilters = {
         @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={Controller.class})
    },useDefaultFilters = false)
    
    image.png

    FilterType中的一些过滤规则

    在前面排除和包含两个测试中,都使用到了@Filter注解,中的type属性来指定过滤的规则。细心的小伙伴在操作的过程中可能发现了,这个FilterType是一个枚举类型。那么其中的这些枚举参数都代表什么意思呢?

    FilterType.ANNOTATION: 按照注解进行包含或者排除类注入到容器

    这个在前面已经多次使用了,就不再重复的写了。

    FilterType.ASSIGNABLE_TYPE: 按照指定的类型进行包含或者排除类注入到容器

    如果想要在包扫描的时候只包含某一种类型的对象被注入到容器中,则可以通过这个枚举值来指定
    @ComponentScan(value="com.gaoxiaobai",includeFilters={
     //在进行包扫描的时候,只会将BookDao类型的对象注入到容器中
     @ComponentSacn.Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookDao.class})
    },useDefaultFilters=false)