002Spring注解@ComponentScan

1、简介

我们开发一直在用@Repository、@Service、@Component、@Controller四个注解,我们也知道要想让这四个注解生效必须配置context:component-scan>标签。这节让注解代替此配置。

2、项目结构

002Spring注解@ComponentScan_第1张图片
image.png

3、基础类

@Repository
public class BookDAO {
}

@Service
public class BookService {
}

@Controller
public class BookController {
}

4、配置文件的方式

4.1、配置文件




    
    


4.2、测试类

private static final String CONFIG = "classpath:component/beans.xml";

@Test
public void test1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(CONFIG);
    String[] names = applicationContext.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
}

4.3、运行结果

bookController
bookDAO
bookService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory

PS:

我们可以看到

bookController
bookDAO
bookService

但是我们还看了一些其他的类,那些是Spring内置类,先不管。

5、注解的方式

5.1、配置类

/**
 * @ComponentScan注解,就代替了这个标签
 *
 * 里面的value值等同于 base-package="com.chentongwei.spring.annotation.component",为什么不是basePackages而是value呢? 我们看下源码
 *
 * @AliasFor("basePackages")
 * String[] value() default {}; 
 * 
 * @AliasFor("value")
 * String[] basePackages() default {};
 *
 * 可以看出value==basePackages,因为用了@AliasFor注解,代表别名的意思。
 */
@ComponentScan(value = "com.chentongwei.spring.annotation.component")
public class MainConfig {

}

5.2、测试类

@Test
public void test2() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    String[] names = applicationContext.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
}

5.3、运行结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookController
bookDAO
bookService

PS:

我们可以看到比上面配置文件的方式多出了一个mainConfig,这是为什么呢?是因为我们MainConfig类被@ComponentScan注解所修饰。

6、高级

6.1、简介

我们知道配置文件的方式可以指定排除哪些包或者哪些类等,只包含哪些包或哪些类等等这些操作。那么注解的方式一样支持。

6.2、排除

6.2.1、需求

排除被注解Service或者Controller所修饰的类。

6.2.2、配置类

@ComponentScan(
    value = "com.chentongwei.spring.annotation.component",
    excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class, Controller.class})
    }
)
public class MainConfig {

}

PS:

excludeFilters属性

6.2.3、测试类

同【5.2、测试类】

6.2.4、输出结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookDAO

PS:

bookService和bookController不见了。

6.3、只包含

6.3.1、需求

只注册包含注解Repository所修饰的类。

6.3.2、配置类

@ComponentScan(
    value = "com.chentongwei.spring.annotation.component",
    includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Repository.class})
    }
)
public class MainConfig {

}

PS:

includeFilters属性

6.3.3、测试类

同【5.2、测试类】

6.3.4、输出结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookController
bookDAO
bookService

PS:

没起作用,为嘛呢?我们回想一下配置文件的方式是不是也不起作用,答案:是的。因为我们需要禁用默认配置,同样的注解方式也需要禁用掉默认配置,源码采取的是默认配置,如下:

boolean useDefaultFilters() default true;

6.3.5、修改配置类

@ComponentScan(
    value = "com.chentongwei.spring.annotation.component",
    useDefaultFilters = false,
    includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Repository.class})
    }
)
public class MainConfig {

}

6.3.6、测试类

同【5.2、测试类】

6.3.7、输出结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookDAO

6.4、其他

我们不只是能按照注解的方式来配置包含排除,我们还可以按照正则表达式的方式,不举例。

7、补充

如何同时制定多个@ComponentScan?

有两种方式:

7.1、Java8支持注解继承

@ComponentScan(
    value = "com.chentongwei.spring.annotation.component",
    useDefaultFilters = false,
    includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Repository.class})
    }
)
@ComponentScan(
    value = "com.chentongwei.spring.annotation.test",
    useDefaultFilters = false,
    includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class})
    }
)
public class MainConfig {

}

PS:

太明显了,两个注解混用,第一个扫描component包下的Repository注解,第二个扫描test包下的Service注解,两个会自动结合(Java8继承)。

7.2、@ComponentScans

@ComponentScans(
    @ComponentScan(
        value = "com.chentongwei.spring.annotation.component",
        useDefaultFilters = false,
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Repository.class})
    }
    ),
    @ComponentScan(
        value = "com.chentongwei.spring.annotation.test",
        useDefaultFilters = false,
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class})
        }
    )
)
public class MainConfig {

}

PS:

采取@ComponentScans注解,包含多个@ComponentScan。

8、细说FilterType过滤规则

8.1、FilterType源码

public enum FilterType {

    /**
     * 注解
     */
    ANNOTATION,

    /**
     * 类型
     */
    ASSIGNABLE_TYPE,

    /**
     * ASPECTJ的方式
     */
    ASPECTJ,

    /**
     * 正则表达式
     */
    REGEX,

    /**
     * 自定义规则 
     */
    CUSTOM

}

PS:

不难发现我们可以使用如上那些方式来指定过滤规则,上面我们一直讲解的是第一种:ANNOTATION注解的方式,接下来我们说下其他几种。

8.2、ASSIGNABLE_TYPE

8.2.1、需求

只要是BookService类型的都加载。

8.2.2、代码

@Service
public class BookService {
}

public class BookServiceImpl extends BookService {
}

@ComponentScan(
    value = "com.chentongwei.spring.annotation.component",
    useDefaultFilters = false,
    includeFilters = {
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
    }
)
public class MainConfig {

}

8.2.3、输出结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookService
bookServiceImpl

8.2、ASPECTJ

用的不多,不讲解。

8.3、REGEX

正则表达式,随用随Google,不举例。

8.4、CUSTOM

8.4.1、需求

只要是类名包含“er”的都加载。

8.4.2、代码

/**
 * 自定义过滤规则需要实现TypeFilter接口。并重写match方法进行匹配,返回true的就加载,返回false的排除。
 * 
 * 可以利用metadataReader和metadataReaderFactory来获取到类的任何信息,以此来书写过滤规则来判断是否需要加载。
 */
public class MyTypeFilter implements TypeFilter {

    /**
     * @param metadataReader:读取到的当前正在扫描的类的信息
     * @param metadataReaderFactory:获取到其他任何类信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        /**
         * 获取当前类注解信息
         */
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

        /**
         * 获取当前类的类信息
         */
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        /**
         * 获取当前类资源信息,比如类路径等资源。
         */
        Resource resource = metadataReader.getResource();

        String className = classMetadata.getClassName();
        System.out.println("className:---->" + className);
        if (className.contains("er")) {
            return true;
        }
        return false;
    }
}

@ComponentScan(
    value = "com.chentongwei.spring.annotation.component",
    useDefaultFilters = false,
    includeFilters = {
            @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
    }
)
public class MainConfig {

}

8.4.3、输出结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
myTypeFilter
bookController
bookService
bookServiceImpl

9、广告

  • 码云点star,万分感谢!

    代码已经上传到码云了

    https://gitee.com/geekerdream/spring-anonation

    通用的权限处理框架

    https://gitee.com/geekerdream/common-security

    通用的异常处理

    https://gitee.com/geekerdream/exception-handler

    通用的发送邮件

    https://gitee.com/geekerdream/common-boot-email

    陆续会推出更多干货,希望关注!

  • QQ群【Java初学者学习交流群】:458430385

  • 微信公众号【Java码农社区】

img
  • 今日头条号:编程界的小学生

你可能感兴趣的:(002Spring注解@ComponentScan)