揭密springboot自動裝配
- 揭密springboot自動裝配(1)--ImportSelector
- 揭密springboot自動裝配(2)--AutoConfigurationImportSelector
- 揭密springboot自動裝配(3)--ioc及調用selectImposts
- 揭密springboot自動裝配(4)--ioc及創建beanFactory
- 揭密springboot自動裝配(5)--ioc及@Autowired註解
在講這個之前,我們先來個例子熱熱身
首先我們先來了解下ImportSelector這個接口的應用,ImportSelector接口是spring中導入外部配置的核心接口,在SpringBoot的自動化配置和@EnableXXX(功能性註解)都有它的存在,具體怎麼用下面走個例子看看
1.實現下ImportSelector
public class UserImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{UserA.class.getName()};
}
}
這裏我們看到有個selectImports方法,我們需要實現它,返回內容我們可以看到就是個數組,把需要裝配進spring容器中的bean的className放進返回數組即可
2.接着我們在啓動類中添加@Import(UserImportSelector.class)
@SpringBootApplication
@Import(UserImportSelector.class)
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
Object userA = run.getBeanFactory().getBean(UserA.class);
System.out.println(userA.toString());
Object userB = run.getBeanFactory().getBean(UserB.class);
System.out.println(userB.toString());
}
}
這裏UserB我是沒有放在selectImports中的,目的是做下對比
3.跑下我們的程序看看結果
這裏你會發現UserA可以被拿到,證明已經交給spring容器中可以拿到,而UserB我沒有任何處理,是拿不到的這個毫無疑問到這裏肯定有人會問,直接@Import(UserA.class)不就行了,搞那麼複雜,嗯,這個沒錯,甚至我可以跑給你們看下
@SpringBootApplication
@Import({UserImportSelector.class,UserB.class})
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
Object userA = run.getBeanFactory().getBean(UserA.class);
System.out.println(userA.toString());
Object userB = run.getBeanFactory().getBean(UserB.class);
System.out.println(userB.toString());
}
}
那爲什麼搞那麼複雜?
ImportSelector主要是實現些比較複雜有邏輯性的bean裝載,我們可以在selectImports做下邏輯判斷,比如@ComponentScan像這個掃描器,我們自己來寫一個和它差不多的東西玩玩,起名@UserScan
1.創建註解@UserScan,引用@Import(UserImportSelector.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import(UserImportSelector.class)
public @interface UserScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
}
2.實現UserImportSelector
public class UserImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(UserScan.class.getName());
if(CollectionUtils.isEmpty(annotationAttributes))return new String[0];
String[] basePackages = (String[]) annotationAttributes.get("basePackages");
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AssignableTypeFilter(Object.class));//這裏實現包含,相當@ComponentScan includeFilters
//scanner.addExcludeFilter(new AssignableTypeFilter(Object.class));//這裏可以實現排除,相當@ComponentScan excludeFilters
Set<String> classes = new HashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
candidateComponents.forEach(e-> {
classes.add(e.getBeanClassName());
});
}
return classes.toArray(new String[classes.size()]);
//return new String[]{UserA.class.getName()};
}
}
3.使用@UserScan("com.example.demo.service"),表示掃描包下的類
@SpringBootApplication
@UserScan("com.example.demo.service")
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
Object userA = run.getBeanFactory().getBean(UserA.class);
System.out.println(userA.toString());
Object userB = run.getBeanFactory().getBean(UserB.class);
System.out.println(userB.toString());
}
}
4.我們跑起來看看結果
這樣可以看到我們是完全可以在spring容器裏面拿到UserA和UserB的
好了,熱身例子到這裏下文將會從源碼上分析springboot自動裝載的實現,主要和我們前面講的ImportSelector接口有關,其中有個叫做AutoConfigurationImportSelector的東西,下文將會提到