为什么spirng扫描到@compent会把对象注册到容器上

为什么spirng扫描到@compent会把对象注册到容器上

spring 扫描方式有多少种?

首先spring是可以通过xml方式和注解方式来扫描指定的包

1.xml形式:

applicationContext.xml文件

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

2.注解方式

@ComponentScan(basePackages = "com.onion")

源码解析

​ 最后会触发:ComponentScanBeanDefinitionParser.parse去解析xml , 如果想知道为什么会触发ComponentScanBeanDefinitionParser的小伙伴可以看: https://www.cnblogs.com/dabenxiang/p/11038914.html

public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
    private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
    private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";
    private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";
    private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";
    private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";
    private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";
    private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";
    private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";
    private static final String INCLUDE_FILTER_ELEMENT = "include-filter";
    private static final String FILTER_TYPE_ATTRIBUTE = "type";
    private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";

    //注释
    public ComponentScanBeanDefinitionParser() {
    }

    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        String basePackage = element.getAttribute("base-package");
        basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
        String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ",; 	
");
        ClassPathBeanDefinitionScanner scanner = this.configureScanner(parserContext, element);
        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
        this.registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
        return null;
   }
    
}

​ 可以看到这里他是Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); ,扫描包,并得到Set<BeanDefinitionHolder>集合。

scanner.doScan的代码展示:

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    ...
 protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
        String[] var3 = basePackages;
        int var4 = basePackages.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String basePackage = var3[var5];
            Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
            ...
        }

        return beanDefinitions;
    }
    
    

}
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {

    ...
    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        return this.componentsIndex != null && this.indexSupportsIncludeFilters() ? this.addCandidateComponentsFromIndex(this.componentsIndex, basePackage) : 
       //调用scanCandidateComponents
       this.scanCandidateComponents(basePackage);
    }
    
    
    ...
        
    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
        LinkedHashSet candidates = new LinkedHashSet();
        String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        //扫描包,得到相应的类信息
        Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath);
        Resource[] var7 = resources;
        int var8 = resources.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            Resource resource = var7[var9];
			
            if (resource.isReadable()) {
				//得到类的元数据信息
                    MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource);
                //判断条件,如果满足加入到候选人处
                    if (this.isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) {
                            //加入到集合
                            candidates.add(sbd);
                        } 
                    }

            }
            return candidates;
    }

        ...
}

​ 从代码中可以看到scanner.doScanClassPathScanningCandidateComponentProvider.findCandidateComponents(basePackage)再到ClassPathScanningCandidateComponentProvider.scanCandidateComponents(basePackage)。 注意: ClassPathBeanDefinitionScanner是ClassPathScanningCandidateComponentProvider的子类

this.scanCandidateComponents 前面部分用途就是扫描basepackage,并从中得到相应的类,并且把相应的类信息,转换成元数据MetadataReader,

然后再通过this.isCandidateComponent(metadataReader) 来判断这个类是否需要加入到容器里,

ClassPathScanningCandidateComponentProvider.isCandidateComponent 代码展示

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {

    //构造方法
    public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
        if (useDefaultFilters) {
            this.registerDefaultFilters();
        }
    }
    
    //这里可以看到注入了@Component
    protected void registerDefaultFilters() {
        this.includeFilters.add(new AnnotationTypeFilter(Component.class));

    }
   
    
    //判断
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        Iterator var2 = this.excludeFilters.iterator();

        TypeFilter tf;
        do {
            if (!var2.hasNext()) {
                var2 = this.includeFilters.iterator();  //遍历includeFilters,然后跟metadataReader做比对

                do {
                    if (!var2.hasNext()) {  
                        return false;
                    }

                    tf = (TypeFilter)var2.next();
                } while(!tf.match(metadataReader, this.getMetadataReaderFactory()));

                return this.isConditionMatch(metadataReader);
            }

            tf = (TypeFilter)var2.next();
        } while(!tf.match(metadataReader, this.getMetadataReaderFactory()));

        return false;
    }
}

​ 从ClassPathScanningCandidateComponentProvider的构造方法再到registerDefaultFilters里可以看出,默认情况下,spring 会调用 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); 这个代码。而isCandidateComponent 则是遍历includeFilters里面的注解,再去跟传进来的类元数据做对比,如果类元数据存在该注解,返回true,就是需要这个类加入容器。

​ 总结说明:这就是说明了只要加入了@Component注解的类就能注入到容器中,@Service , @Controller 等等都是继承了@Component,所以加上这些注解的类都可以注入到容器里。

​ 那如果我们想自定义一个注解且不继承@Component, 怎么让带上这个自定义注解的类注入到容器里呢?

带上自定义注解,注入到容器

openFeign的代码示例

​ 其实openFeign,也是做过这样的事情,只要我们仿造openFeign就可以达到这样的目的。

@SpringBootApplication
@EnableFeignClients
@EnableMyBeanAnno
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
@FeignClient(value = "spring-cloud-order-service")
public interface OpenFeignService {
    @GetMapping("/orders")
    public String getAllOrder();
}

@FeignClient 就是属于openFeign的自定义标签

@RestController
public class OpenFeignController {
    @Autowired
    private OpenFeignService openFeignService;

    @GetMapping("/orders")
    public String getAllOrder(){
        return openFeignService.getAllOrder();
    }
}

这段代码可以直接注入OpenFeignService,证明OpenFeignService已经注入到容器里。

源码解析

为何带上@FeignClient的接口能注入到容器里:

首先看@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};

    String[] basePackages() default {};

}

​ 根据@Import的第三种用法,会加载FeignClientsRegistrar.registerBeanDefinitions的方法,因为FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar ,

提示:

@Import使用方法解析: https://www.cnblogs.com/yichunguo/p/12122598.html

FeignClientsRegistrar代码:

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    ...
  public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        this.registerDefaultConfiguration(metadata, registry);
        this.registerFeignClients(metadata, registry);
    }
    
    ...
    
   protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {
                    isCandidate = true;
                }

                return isCandidate;
            }
        };
    }
    
    ...
        
        public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        //获取EnableFeignClients的属性
        Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        //构造AnnotationTypeFilter参数是FeignClient.class
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
        Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
        Object basePackages;
         if (clients != null && clients.length != 0) {
                ....

         }
      else {
          //加入到includeFilter里面去
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = this.getBasePackages(metadata);
        }

        Iterator var17 = ((Set)basePackages).iterator();

        while(var17.hasNext()) {
            String basePackage = (String)var17.next();
            //扫描包。返回可以注入到容器中的BeanDefinition
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            Iterator var21 = candidateComponents.iterator();

            while(var21.hasNext()) {
                BeanDefinition candidateComponent = (BeanDefinition)var21.next();
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
                    String name = this.getClientName(attributes);
                    this.registerClientConfiguration(registry, name, attributes.get("configuration"));
                    //注入到容器里
                    this.registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }

    }

}

可以看到FeignClientsRegistrar.registerBeanDefinitions 调用 this.registerFeignClients

解析this.registerFeignClients

第一: 获取EnableFeignClients的属性,来确定要扫描的包是什么。

第二: 把FeignClient.class 加入到 scanner中

第三: scanner.findCandidateComponents(basePackage)。 返回加上了FeignClient的类信息的BeanDefinition

第四:把带上FeignClient注解的类注入到容器里

scanner.findCandidateComponents就不做解析了。因为上面解析@compent已经解析过了

自制自定义注解

首先仿照@EnableFeignClients , 新建一个@EnableMyBeanAnno

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MyImportBeanDefinitionRegistrar.class})
public @interface EnableMyBeanAnno {

    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}

仿照@FeignClientsRegistrar , 新建一个@MyImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    private ResourceLoader resourceLoader;

    private Environment environment;


    public  void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        //加入MyBeanAnno.class到includeFilter中
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(MyBeanAnno.class);
        scanner.addIncludeFilter(annotationTypeFilter);
        
        //这里我就直接指定要读的包了。不搞那么麻烦了
        Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents("com.onion.test");

        for (BeanDefinition candidateComponent : candidateComponents) {
            beanDefinitionRegistry.registerBeanDefinition(candidateComponent.getBeanClassName(),candidateComponent);

        }


    }


    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {
                    isCandidate = true;
                }

                return isCandidate;
            }
        };
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

}

带上@MyBeanAnno注解的类

@MyBeanAnno
public class MyBean {
}

测试类:

@SpringBootApplication
@EnableFeignClients
@EnableMyBeanAnno  //带上了我们自定义的注解
public class App {


    @Autowired
    private StaticApp staticApp;


    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }




    @Component
    public class StaticApp implements ApplicationContextAware {

        private ApplicationContext applicationContext;

        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            MyBean bean = applicationContext.getBean(MyBean.class);
            System.out.println(bean);
        }


    }


}

最后的结果:

原文地址:https://www.cnblogs.com/dabenxiang/p/13616491.html