Spring Boot 多模块注入访问不到jar包中的Bean问题(巧用@ComponentScan)

原因:导致此类问题的主要原因是,此类不在Spring Boot的组件扫描范围之内。

目录

1. 关于 Spring Boot 自动注入及组件扫描

2. Spring Boot 中 @ComponentScan

3. Spring Boot 中 @SpringBootApplication

4 @ComponentScans

5 具体使用例子

6 补充说明:@AliasFor


1. 关于 Spring Boot 自动注入及组件扫描

在平时使用 Spring Boot 时,常常会使用到@Configuration,@Contoller,@Service,@Component等注解,被添加这些注解的类,在 Spring Boot 启动时,会自动被 Spring 容器管理起来。

上面提到了,添加了一些注解的类会在Spring Boot 容器启动时,被加载到Spring 容器中。那么,组件扫描的作用就是:当 Spring Boot 启动时,根据定义的扫描路径,把符合扫描规则的类装配到spring容器中。

2. Spring Boot 中 @ComponentScan

@ComponentScan有如下属性:
 

value:指定要扫描的package;
includeFilters=Filter[]:指定只包含的组件
excludeFilters=Filter[]:指定需要排除的组件;
useDefaultFilters=true/false:指定是否需要使用Spring默认的扫描规则:被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;

在过滤规则Filter中:
FilterType:指定过滤规则,支持的过滤规则有
    ANNOTATION:按照注解规则,过滤被指定注解标记的类;
    ASSIGNABLE_TYPE:按照给定的类型;
    ASPECTJ:按照ASPECTJ表达式;
    REGEX:按照正则表达式
    CUSTOM:自定义规则;
value:指定在该规则下过滤的表达式;

@ComponentScan过滤规则说明

规则表达式说明

1. 扫描指定类文件
   @ComponentScan(basePackageClasses = Person.class)
2. 扫描指定包,使用默认扫描规则,即被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;
   @ComponentScan(value = "com.yibai")
3. 扫描指定包,加载被@Component注解标记的组件和默认规则的扫描(因为useDefaultFilters默认为true)
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
4. 扫描指定包,只加载Person类型的组件
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = Person.class) }, useDefaultFilters = false)
5. 扫描指定包,过滤掉被@Component标记的组件
   @ComponentScan(value = "com.yibai", excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
6. 扫描指定包,自定义过滤规则
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }, useDefaultFilters = true)

简单的介绍了@ComponentScan的基础作用,这个注解为我们使用提供了一些可自定义配置属性,先来看看@ComponentScan注解源码:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
  	// 指定扫描包的位置(同:basePackages 属性),可以是单个路径,也可以是扫描的路径数组
    @AliasFor("basePackages")
    String[] value() default {};
	// 指定扫描包的位置(同:value 属性)
    @AliasFor("value")
    String[] basePackages() default {};
	// 指定具体的扫描的类
    Class[] basePackageClasses() default {};
	// bean的名称的生成器	
    Class nameGenerator() default BeanNameGenerator.class;

    Class scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
	// 控制符合组件检测条件的类文件   默认是包扫描下的  **/*.class
    String resourcePattern() default "**/*.class";
    // 是否开启对@Component,@Repository,@Service,@Controller的类进行检测
    boolean useDefaultFilters() default true;
    // 包含的过滤条件
    // 1. FilterType.ANNOTATION:			按照注解过滤
    // 2. FilterType.ASSIGNABLE_TYPE:	    按照给定的类型
    // 3. FilterType.ASPECTJ:				使用ASPECTJ表达式
    // 4. FilterType.REGEX:					正则
    // 5. FilterType.CUSTOM:				自定义规则
    ComponentScan.Filter[] includeFilters() default {};
    // 排除的过滤条件,用法和includeFilters一样
    ComponentScan.Filter[] excludeFilters() default {};

    boolean lazyInit() default false;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;
        @AliasFor("classes")
        Class[] value() default {};
        @AliasFor("value")
        Class[] classes() default {};
        String[] pattern() default {};
    }
}

总结一下@ComponentScan的常用方式如下:

  • 通过使用value,basePackages属性来指定扫描范围;

  • 自动扫描路径下边带有@Controller,@Service,@Repository,@Component注解加入Spring容器

  • 通过includeFilters加入扫描路径下没有以上注解的类加入spring容器

  • 通过excludeFilters过滤出不用加入spring容器的类

  • 自定义增加了@Component注解的注解方式

3. Spring Boot 中 @SpringBootApplication

在创建Spring Boot 项目之后,在默认的启动类上会被添加@SpringBootApplication注解,这个注解默认帮我们开启一些自动配置的功能,比如:基于Java的Spring配置,组件扫描,特别是用于启用Spring Boot的自动配置功能。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration                    // 允许自动配置
@ComponentScan(
    excludeFilters = {@Filter(              // 定义排除规则
    type = FilterType.CUSTOM,               // 采用自定义的方式
    classes = {TypeExcludeFilter.class}     // 自定义实现逻辑
), @Filter(                                 // 同上
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    
    // 为 @EnableAutoConfiguration 添加 exclude 规则
    @AliasFor(
        annotation = EnableAutoConfiguration.class,
        attribute = "exclude"
    )
    Class[] exclude() default {};
    
    // 为 @EnableAutoConfiguration 添加 excludeName 规则
    @AliasFor(
        annotation = EnableAutoConfiguration.class,
        attribute = "excludeName"
    )
    String[] excludeName() default {};
    
    // 为 @ComponentScan 添加 basePackages 规则
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    // 为 @ComponentScan 添加 basePackageClasses 规则
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class[] scanBasePackageClasses() default {};
}

从上面的源码部分可以看到,@SpringBootApplication是一个组合注解,也就相当于使用一个@SpringBootApplication可以替代@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan几个注解联合使用。

那么,可能会有这样的问题,我只是使用了一个@SpringBootApplication注解,但是我如何对@ComponentScan的属性做自定义配置呢?

当然,Spring 团队已经很好的解决了这个问题,在@SpringBootApplication注解类中的属性上添加@AliasFor注解,从而实现通过对@SpringBootApplication中的属性进行自定义,达到对对应的注解的属性的自定义。
比如:

@AliasFor(
    annotation = ComponentScan.class,
    attribute = "basePackages"
)
String[] scanBasePackages() default {};

这段代码就是实现,通过对@SpringBootApplication的属性scanBasePackages,实现对@ComponentScan中的属性basePackages进行自定义。

4 @ComponentScans

可以一次声明多个@ComponentScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)  //指定ComponentScan可以被ComponentScans作为数组使用
public @interface ComponentScan {
}
 
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {
    
	ComponentScan[] value();
 
}

例如:

@ComponentScans(value = { @ComponentScan(value = "com.yibai.spring.annotation"),
		@ComponentScan(value = "com.yibai.spring.annotation", includeFilters = {
				@Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }) })
public class MainConfig {
 
	@Bean(name = "pers", initMethod = "init", destroyMethod = "destory")
	public Person person() {
		return new Person();
	}
 
}

 

5 具体使用例子


先看看项目结构,项目入口文件在子项目A-project中,并且入口类所在包位置为:package com.aa.bb.cc。

也就是说,在不做任何配置的情况下,此项目只会扫描当前包路径及其子路径下的文件,并将符合条件的对象注入到容器中管理。

再看看配置文件所在的包路径位置:package aa.bb.dd.ee.ff,可见此包路径并不在项目扫描的路径范围之内。

这也就导致了,我们定义的配置类,虽然加了@Configuration也不会对我们的项目起到作用。

可以对项目注解进行稍微修改,制定扫描包的范围,就可以简单的解决这个问题。如下:

@SpringBootApplication(scanBasePackages="aa.bb")
public class MyApplication {

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

6 补充说明:@AliasFor

在Spring注解中,经常会发现很多注解的不同属性起着相同的作用,比如@ComponentScan的value属性和basePackages属性。所以在使用的时候就需要做一些基本的限制,比如value和basePackages的值不能冲突,比如任意设置value或者设置basePackages属性的值,都能够通过另一个属性来获取值等等。为了统一处理这些情况,Spring创建了@AliasFor标签。

 

你可能感兴趣的:(Spring,Boot)