文章目录
1. 关于 SpringBoot 自动注入及组件扫描
在平时使用 Spring Boot 时,常常会使用到@Configuration
、@Contoller
,@Service
,@Component
等注解,被添加这些注解的类,在 SpringBoot 启动时,会自动被 Spring 容器管理起来。组件扫描的作用就是:当 SpringBoot 启动时,根据定义的扫描路径,把符合扫描规则的类装配到 Spring 容器中。
2. SpringBoot 中的 @ComponentScan
简单的介绍了@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<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> 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. SpringBoot 中的 @SpringBootApplication
在创建 SpringBoot 项目之后,在默认的启动类上会被添加@SpringBootApplication
注解,这个注解默认帮我们开启一些自动配置的功能,比如:基于 Java 的 Spring 配置,组件扫描,特别是用于启用 SpringBoot 的自动配置功能。
@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
几个注解联合使用。
注:此注释从 SpringBoot 1.2 开始提供,这意味着如果你运行的是较低的版本,并且需要这些功能,则需要手动添加@Configuration,@CompnentScan和@EnableAutoConfiguration。
那么,可能会有这样的问题,我只是使用了一个@SpringBootApplication
注解,但是我如何对@ComponentScan
的属性做自定义配置呢?
当然,Spring 团队已经很好的解决了这个问题,在@SpringBootApplication
注解类中的属性上添加@AliasFor
注解,从而实现通过对@SpringBootApplication
中的属性进行自定义,达到对对应的注解的属性的自定义。
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
这段代码就是实现,通过对@SpringBootApplication
的属性scanBasePackages
,实现对@ComponentScan
中的属性basePackages
进行自定义。
@AliasFor
在 Spring 注解中,经常会发现很多注解的不同属性起着相同的作用,比如 @ComponentScan 的 value 属性和 basePackages 属性。所以在使用的时候就需要做一些基本的限制,比如 value 和 basePackages 的值不能冲突,比如任意设置 value 或者设置 basePackages 属性的值,都能够通过另一个属性来获取值等等。为了统一处理这些情况,Spring 创建了 @AliasFor 标签。
4. 多模块注入示例
我这有个多模块项目 data-output,内部有三个模块,一个公共模块 output-common,两个web模块 output-manager,output-backstage。这三个模块都是 SpringBoot 项目。
output-manager,output-backstage 通过 pom 依赖的方式使用 output-common 中的类。
这三个项目内部的基础包路径分别是:com.gtcom.common、com.gtcom.manager、com.gtcom.backstage。
此时output-manager,output-backstage 的启动类注解上需要配置下面内容才能正常使用公共包中的类:
@SpringBootApplication(scanBasePackages = {"com.gtcom"})
5. 多模块注入示例一
项目入口文件在子项目 security-demo 中,并且入口类所在包位置为:com.github.jdkong.security
。也就是说,在不做任何配置的情况下,此项目只会扫描当前包路径及其子路径下的文件,并将符合条件的对象注入到容器中管理。
再看看配置文件所在的包路径位置:com.github.jdkong.browser.config
,可见此包路径并不在项目扫描的路径范围之内。这也就导致了,我们定义的配置类,虽然加了@Configuration
也不会对我们的项目起到作用。
可以对项目注解进行稍微修改,制定扫描包的范围,就可以简单的解决这个问题。如下:
@SpringBootApplication(scanBasePackages="com.github.jdkong")
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class,args);
}
}
6. 多模块注入示例二
SpringBoot 中 Service 自动注入很方便,例:
Service.class(接口类)
ServiceImpl.class(实现类)
Controller.class(使用类)
用以上三个类来说一下自动注入:
单体项目:分别ServiceImpl
头上添加@Service
,Controller
中Service
对象添加@Autowired
即可使用。
多模块项目:三个(种)类分别在三个 module 下:
moduleA : Service.class(com.example.moduleA )
moduleB : ServiceImpl.class ( com.example.moduleB )
moduleC : Controller.class ( com.example.moduleC )
此时 B 依赖 A,C 依赖 B,在 pom.xml 中添加好依赖关系。
如何自动注入?
1、接口、实现、使用类,姿势不变,按单项目方式写即可;
2、在 moduleC 的 Application 启动类中做修改!
因为 Service 及 ServiceImpl 均在com.example
包下(C 可以不在),可以看作是同一次 scan 过程;所以解决办法:
@SpringBootApplictaion(scanBasePackages="com.example")
或者
@ComponentScan(value = "com.example")
若是这样写scanBasePackages={" com.example.moduleA , com.example.moduleB "}
,则会失败;