项目外部指的是非SpringBootApplication应用主程序类所在同层级包下的类,当我们启动一个SpringBootApplication应用主程时,应用程序会自动扫描该类同层级包下的所有类,并将带有@Configuation、@Component以及@Component的扩展注解(@Controller、@Service、@Respistory等等)注解的类注册Bean,并纳入Spring Ioc容器,此外配置类中被@Bean标注的方法也会被注册为Bean,并纳入Spring Ioc容器
那我们如何才能将项目外部的配置类注册成Bean并纳入到自己的Spring Ioc容器呢?目前我知道的有三种方法,分别是@ComponentScan、spring.factories、@Import。下面我将分析这三种方法的执行逻辑,以便理解。
1、@ComponentScan
通过在SpringBootApplication应用主程序上添加 @ComponentScan 注解并在参数中填入外部配置类包的全限定类名,让主程序在启动时主动的根据该路径去扫描,如果扫描到带有@Configuation或者@Component等注解的类,则将其注册为Bean,并纳入自己的Spring Ioc容器。
@SpringBootApplication
@ComponentScan(basePackages = {
"com.xxx.xxx.config"})
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
2、spring.factories
通过在Resource文件夹下新建META-INF文件夹并在其中新建spring.factories配置文件。该配置文件的作用是当我们将项目外部功能模块作为依赖引入到本项目时,主程序会通过加载指定路径下的配置文件也就是Resource->META-INF->spring.factories路径,最终通过加载spring.factories文件来确定引入的依赖中哪些配置类需要注册为Bean,并纳入自己的Spring Ioc容器。这也是为什么我们在引入了第三方的starter依赖时,也能够使用一些不是自己注册的Bean的原因。
// 加载到此配置时会将com.blog.common.mybatis路径下的MybatisPlusAutoConfiguation配置类注册为Bean并纳入自己IOC容器
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.blog.common.mybatis.MybatisPlusAutoConfiguation
3、@Import
@Import注解最常见的就是用在注解上,在使用注解时,应用程序在扫描到注解后会将@Import注解引入的配置类注册成Bean,同时将该配置类下被@Bean注解标注的方法也一起注册为Bean,并纳入自己的Spring Ioc容器。一般来说,被@Import注解标注的注解都带有Enable前缀,用于开启某种功能,比如:@EnableDiscoveryClient注解就是用来开启服务发现功能,具体这个注解干了什么,我们通过分析其源码不难发现,这个注解其实就是通过@Import引入了EnableDiscoveryClientImportSelector.class这个导入选择器,然后通过这个导入选择器去选择注入一些Bean
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({EnableDiscoveryClientImportSelector.class}) // 引入导入选择器
public @interface EnableDiscoveryClient {
boolean autoRegister() default true;
}
// 我们在使用@Import注解时也可以直接导入配置类
@Order(2147483547)
public class EnableDiscoveryClientImportSelector extends SpringFactoryImportSelector<EnableDiscoveryClient> {
public EnableDiscoveryClientImportSelector() {
}
// 具体的导入选择执行方法
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata);
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(this.getAnnotationClass().getName(), true));
boolean autoRegister = attributes.getBoolean("autoRegister");
if (autoRegister) {
List<String> importsList = new ArrayList(Arrays.asList(imports));
importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
imports = (String[])importsList.toArray(new String[0]);
} else {
Environment env = this.getEnvironment();
if (ConfigurableEnvironment.class.isInstance(env)) {
ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
LinkedHashMap<String, Object> map = new LinkedHashMap();
map.put("spring.cloud.service-registry.auto-registration.enabled", false);
MapPropertySource propertySource = new MapPropertySource("springCloudDiscoveryClient", map);
configEnv.getPropertySources().addLast(propertySource);
}
}
return imports;
}
protected boolean isEnabled() {
return (Boolean)this.getEnvironment().getProperty("spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
}
protected boolean hasDefaultFactory() {
return true;
}
}