包掃描@ComponentScan
+組件標注注解(@Controller
、@Service
、@Repository
、@Component
)
包掃描不是必須的,指定包名后以指定的包名為准,比如指定包名為a:@ComponentScan("a")
,即使b包中有標注@Controller
等注解的bean也不注冊。
包掃描注解除了默認的value()
還有basePackages()
、basePackageClasses()
等方法:
basePackages()
: 指定掃描的包名(前綴)
basePackageClasses()
:如果指定為A.class
,那么會掃描A類所在包的類。
includeFilters()
:將所指定的類注入容器。
excludeFilters()
:將所指定的類排除。
@ComponentScan.Filter
中的FilterType
除了給定的類型,還可以自定義:type=FilterType.CUSTOM
,指定的類實現TypeFilter
接口即可,同時注意不適用默認攔截器useDefaultFilters = false
。
比如,定義和注入配置文件映射類需要使用注解ConfigurationProperties
和EnableConfigurationProperties
,那么當配置文件變多的時候,注入起來就比較麻煩了@EnableConfigurationProperties(A.class, B.class, C.class...)
,所以可以使用自定義過濾器來注入所有的配置文件映射類,思路很簡單,只要標注注解ConfigurationProperties
就將其注入:
public class IncludePropertiesFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return metadataReader.getAnnotationMetadata().hasAnnotation(EnableConfigurationProperties.class.getName());
}
}
在主啟動類上開啟過濾規則:
@SpringBootApplication
@ComponentScan(useDefaultFilters = false, includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = IncludePropertiesFilter.class))
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Bean
可以放在方法和注解上。
一般放在標有@Configuration
類中的方法上。如:
@Import
只能用在類上,最簡單直觀,將A注入:
雖然直觀,但需要批量注入就有點麻煩,@Import
提供了高級功能:
ImportSelector
ImportSelector是個接口,它只有一個方法:
String[] selectImports(AnnotationMetadata importingClassMetadata);
返回值是需要注入的類型全類名,入參是指和@Import
並列的全部注解信息,比如有個實現類:
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if (importingClassMetadata.hasAnnotation(UsesJava8.class.getName())) {
return new String[]{A.class.getName()};
}
return new String[0];
}
}
表示只有@UsesJava8
和@Import
同時標注時才注入A,以下情況會生效:
@SpringBootApplication
@Import(MyImportSelector.class)
@UsesJava8
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar也是個接口,它只有一個方法:
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
簡單實現如下:
public class MyImportSelector implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("IThreadPool", new RootBeanDefinition(A.class));
}
}
調用:
@SpringBootApplication
@Import(MyImportSelector.class)
@UsesJava8
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
注冊工廠Bean
和普通Bean不一樣,普通的是通過調用構造器,而工廠是實現FactoryBean泛型接口,泛型為真正類型,通過getObject方法獲取實例:
@Configuration
public class MyConfiguration {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
// com.y.pojo.MyConfiguration$A
System.out.println(context.getBean("aFactoryBean").getClass().getName());
// com.y.pojo.MyConfiguration$AFactoryBean
System.out.println(context.getBean("&aFactoryBean").getClass().getName());
A a1 = context.getBean("aFactoryBean", A.class);
A a2 = context.getBean("aFactoryBean", A.class);
// false
System.out.println(Objects.equals(a1, a2));
}
@Bean
public AFactoryBean aFactoryBean() {
return new AFactoryBean();
}
private static class A {
}
private static class AFactoryBean implements FactoryBean<A> {
@Override
public A getObject() throws Exception {
return new A();
}
@Override
public Class<?> getObjectType() {
return A.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
}