本教程先通过xml配置方式,引渡到使用注解的方式,重点讲注解的使用

通过xml配置文件配置包扫描的方式

创建xml配置文件

创建文件:src/main/resources/scan.xml
代码如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. 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">
  6. <context:component-scan base-package="com.tonyliu"/>
  7. </beans>

节点<context:component-scan>指定包扫描,base-package指定要扫描的包范围
base-package指定的包内的类,只要但不限于使用以下下注解,都会被扫描到并注册到IOC容器中
@Controller、@Component、@Service、@Repository

创建需要扫描的类文件

将使用@Controller、@Service、@Repository这三个注解,创建3个类,用于测试扫描包的配置
创建文件:src/main/java/com/tonyliu/controller/BookController.java,代码如下

  1. package com.tonyliu.controller;
  2. import org.springframework.stereotype.Controller;
  3. @Controller
  4. public class BookController {
  5. }

创建文件:src/main/java/com/tonyliu/service/BookService.java,代码如下

  1. package com.tonyliu.service;
  2. import org.springframework.stereotype.Service;
  3. @Service
  4. public class BookService {
  5. }

创建文件:src/main/java/com/tonyliu/dao/BookDao.java,代码如下

  1. package com.tonyliu.dao;
  2. import org.springframework.stereotype.Repository;
  3. @Repository
  4. public class BookDao {
  5. }

引入单元测试依赖

接下来我们来做个测试,将使用单元测试的方式运行程序进行测试
引入单元测试依赖

  1. <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
  2. <dependency>
  3. <groupId>org.junit.jupiter</groupId>
  4. <artifactId>junit-jupiter-api</artifactId>
  5. <version>5.6.2</version>
  6. <scope>test</scope>
  7. </dependency>

书写单元测试方法

书写单元测试方法,打印IOC容器中注册组件的名字,代码如下

  1. @Test
  2. public void test01() {
  3. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scan.xml");
  4. String[] names = applicationContext.getBeanDefinitionNames();
  5. for (String name : names) {
  6. System.out.println(name);
  7. }
  8. }

结果如下,会打印出扫描到的组件名字,忽略其他打印的内容
image.png
变更测试,将xml配置文件中的base-package的值变更为base-package=”com.tonyliu.controller”之后,在运行一下测试程序,结果如下
image.png

通过注解配置包扫描方式

涉及注解 @Configuration @ComponentScan @ComponentScans

主要讲解@ComponentScan注解

@ComponentScan注解说明

  • 将添加特定注解的类注册到IOC容器中
  • @ComponentScan注解和@Configuration注解一同使用
  • 和xml配置文件中的<context:component-scan>元素功能相同
  • 该注解可以设置需要扫描的类、包等和需要排除扫描的类、包等

@ComponentScan注解的定义

  1. package org.springframework.context.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Repeatable;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8. import org.springframework.beans.factory.support.BeanNameGenerator;
  9. import org.springframework.core.annotation.AliasFor;
  10. import org.springframework.core.type.filter.TypeFilter;
  11. @Retention(RetentionPolicy.RUNTIME)
  12. @Target(ElementType.TYPE)
  13. @Documented
  14. @Repeatable(ComponentScans.class)
  15. public @interface ComponentScan {
  16. @AliasFor("basePackages")
  17. String[] value() default {};
  18. @AliasFor("value")
  19. String[] basePackages() default {};
  20. Class<?>[] basePackageClasses() default {};
  21. Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
  22. Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
  23. ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
  24. String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
  25. boolean useDefaultFilters() default true;
  26. Filter[] includeFilters() default {};
  27. Filter[] excludeFilters() default {};
  28. boolean lazyInit() default false;
  29. @Retention(RetentionPolicy.RUNTIME)
  30. @Target({})
  31. @interface Filter {
  32. FilterType type() default FilterType.ANNOTATION;
  33. @AliasFor("classes")
  34. Class<?>[] value() default {};
  35. @AliasFor("value")
  36. Class<?>[] classes() default {};
  37. String[] pattern() default {};
  38. }
  39. }

@ComponentScan注解的使用

创建一个配置类文件
创建文件:src\main\java\com\tonyliu\config\ScanConfig.java

  1. package com.tonyliu.config;
  2. import org.springframework.context.annotation.Configuration;
  3. @Configuration
  4. public class ScanConfig {
  5. }

测试代码

  1. @Test
  2. public void test02(){
  3. ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);
  4. String[] names = applicationContext.getBeanDefinitionNames();
  5. for (String name : names) {
  6. System.out.println(name);
  7. }
  8. }

扫描指定包下可以注册的组件

  1. package com.tonyliu.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. @ComponentScan(value = "com.tonyliu")
  6. public class ScanConfig {
  7. }

value:指定要扫描的包范围com.tonyliu

测试代码运行结果
image.png

排除需要扫描注册的组件

  1. package com.tonyliu.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.FilterType;
  5. import org.springframework.stereotype.Controller;
  6. import org.springframework.stereotype.Repository;
  7. @Configuration
  8. @ComponentScan(value = "com.tonyliu",excludeFilters = {
  9. @ComponentScan.Filter(type=FilterType.ANNOTATION,value = {Controller.class, Repository.class})
  10. })
  11. public class ScanConfig {
  12. }

使用属性excludeFilters排除需要扫描注册的类型:Filter[] excludeFilters() default {};
该属性是一个Filter类型的数组形式,Filter类型在@ComponentScan注解中被定义
Filter中的属性type指定需要按照哪种规则进行筛选
上面是我们示例要排除添加了@Controller@Repository这两个注解的类
tip:直接中的数组类型使用{}包裹,中间用逗号分隔

只包含需要注册的组件

  1. package com.tonyliu.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.FilterType;
  5. import org.springframework.stereotype.Controller;
  6. import org.springframework.stereotype.Repository;
  7. @Configuration
  8. @ComponentScan(value = "com.tonyliu", includeFilters = {
  9. @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class, Repository.class})
  10. }, useDefaultFilters = false)
  11. public class ScanConfig {
  12. }

includeFilters:只包含需要注册的组件规则
注:使用该规则需要将useDefaultFilters设置为false,筛选规则时才是只包含的关系,对应xml配置文件的use-default-filters熟悉,示例

  1. <context:component-scan base-package="com.tonyliu.controller" use-default-filters="false"/>

组合注解的使用

可以设置@ComponentScan的组合模式,详见代码

  1. package com.tonyliu.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.ComponentScans;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.context.annotation.FilterType;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.stereotype.Repository;
  8. @Configuration
  9. @ComponentScans(
  10. value = {
  11. @ComponentScan(value = "com.tonyliu", includeFilters = {
  12. @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})
  13. }, useDefaultFilters = false),
  14. @ComponentScan(value = "com.tonyliu", includeFilters = {
  15. @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Repository.class})
  16. }, useDefaultFilters = false)
  17. }
  18. )
  19. public class ScanConfig {
  20. }

FilterType讲解

  1. public enum FilterType {
  2. /**
  3. * 按照注解规则筛选
  4. */
  5. ANNOTATION,
  6. /**
  7. * 按照类型筛选
  8. */
  9. ASSIGNABLE_TYPE,
  10. /**
  11. * 按照ASPECTJ表达式筛选
  12. */
  13. ASPECTJ,
  14. /**
  15. * 按照正则表达式筛选
  16. */
  17. REGEX,
  18. /** Filter candidates using a given custom
  19. * {@link org.springframework.core.type.filter.TypeFilter} implementation.
  20. * 自定义筛选规则
  21. */
  22. CUSTOM
  23. }

CUSTOM自定义筛选规则的使用

根据文档可知,使用该方式需要实现org.springframework.core.type.filter.TypeFilter这个接口
org.springframework.core.type.filter.TypeFilter接口的定义如下

  1. package org.springframework.core.type.filter;
  2. import java.io.IOException;
  3. import org.springframework.core.type.classreading.MetadataReader;
  4. import org.springframework.core.type.classreading.MetadataReaderFactory;
  5. @FunctionalInterface
  6. public interface TypeFilter {
  7. /**
  8. * Determine whether this filter matches for the class described by
  9. * the given metadata.
  10. * @param metadataReader 读取到的当前的正在扫描的类的信息
  11. * @param metadataReaderFactory 可以获取到其他任何类的信息
  12. * @return whether this filter matches
  13. * @throws IOException in case of I/O failure when reading metadata
  14. */
  15. boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
  16. throws IOException;
  17. }

创建文件实现该接口:

  1. package com.tonyliu.config;
  2. import org.springframework.core.io.Resource;
  3. import org.springframework.core.type.AnnotationMetadata;
  4. import org.springframework.core.type.ClassMetadata;
  5. import org.springframework.core.type.classreading.MetadataReader;
  6. import org.springframework.core.type.classreading.MetadataReaderFactory;
  7. import org.springframework.core.type.filter.TypeFilter;
  8. import java.io.IOException;
  9. public class MyTypeFilter implements TypeFilter {
  10. public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
  11. // 获取当前类注解的信息
  12. AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
  13. // 获取当前正在扫描类的类的信息
  14. ClassMetadata classMetadata = metadataReader.getClassMetadata();
  15. // 获取当前类的资源信息(类的路径等)
  16. Resource resource = metadataReader.getResource();
  17. String className = classMetadata.getClassName();
  18. if (className.equals("com.tonyliu.controller.BookController")) {
  19. return true;
  20. }
  21. return false;
  22. }
  23. }

测试代码运行结果
image.png