@SpringBootApplication注解主要有如下三个注解组成
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
其中@SpringBootConfiguration注解其实就是@Configuration注解,在之前的文章中已经详细说过了,这里不再讨论这个注解
先来看一下@EnableAutoConfiguration,源码如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
可知主要有@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)注解组成,而@AutoConfigurationPackage注解的源码如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage
所以到这来可以看出@EnableAutoConfiguration注解的核心就是@Import(AutoConfigurationPackages.Registrar.class)和@Import(AutoConfigurationImportSelector.class)这2个注解。
AutoConfigurationImportSelector这个类就是导入自动化配置类,主要就是解析类路径下META-INF/spring.factories文件中定义的自动化配置类,导入到spring ioc容器中。
@Import注解:在原生的spring framework中,组件装配有三个阶段
1、spring2.5+:@Component
2、spring3.0+:使用@Configuration+@Bean
3、spring3.1+:@EnableXXX+@Import
第一和第二阶段的方式常用于一个普通的项目,项目中没有那么多需要注入到spring ioc容器的bean对象,而对于一个框架而言,框架中有大量的bean对象需要注入到容器中,这时候第三种组件装配的方式就比较合适
而第三种方式@EnableXXX+@Import有可以分为四种形式
形式1:@EnableXXX+@Import({普通类.class})
public class Apple {
}
public class Potato {
}
//自定义EnableFruit注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//将Apple和Potato类导入到容器中
@Import({Apple.class, Potato.class})
public @interface EnableFruit {
}
//SpringBoot程序启动类
@SpringBootApplication
@EnableFruit
public class Csrf3Application {
public static void main(String[] args) {
SpringApplication.run(Csrf3Application.class, args);
}
}
//测试
@SpringBootTest
class Csrf3ApplicationTests {
@Autowired
Apple apple;
@Autowired
Potato potato;
@Test
void test2(){
System.out.println("apple = " + apple);
System.out.println("potato = " + potato);
}
}
结果:
形式2:@EnableXXX+@Import({配置类.class})
这里只写出和上面代码不同的地方
@Configuration
public class FruitConfig {
@Bean
public Apple apple(){
return new Apple();
}
@Bean
public Potato potato(){
return new Potato();
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({FruitConfig.class})
public @interface EnableFruit {
}
结果:
形式3:@EnableXXX+@Import({实现ImportSelector接口类.class})
public class FruitImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{Apple.class.getName(), Potato.class.getName()};
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({FruitImportSelector.class})
public @interface EnableFruit {
}
结果:
形式4:@EnableXXX+@Import({实现ImportBeanDefinitionRegistrar接口类.class})
public class FruitRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("apple",new RootBeanDefinition(Apple.class));
registry.registerBeanDefinition("potato",new RootBeanDefinition(Potato.class));
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({FruitRegistrar.class})
public @interface EnableFruit {
}
结果:
我们再来看一下@ComponentScan注解,主要就是将@SpringBootApplication标注的类所在的当前包及其子包中的被@Controller,@Service,@Repository,@Component这4个注解标注的类加入到spring容器中,excludeFilters属性就是排除哪些类不需要扫描并装配到spring容器中,源码中有2中类型的类不需要扫描装配到容器中,一是:TypeExcludeFilter.class,如果用户需要将某个类不装配到spring容器中,即使这个类上面被标注上述4个注解中的某一个,就需要自定义类继承TypeExcludeFilter来实现具体的不装配规则。二是:AutoConfigurationExcludeFilter.class,因为在@EnableAutoConfiguration注解中已经将自动化配置类装配到了spring容器中,所有在这里就不需要再次将这些自动化配置类扫描导入到容器中。
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
我们可以通过如下形式来排除不被@ComponentScan注解默认的扫描装配规则来装配bean对象,比如我们不想被@Service注解标注的类加载到spring容器中
@Service
public class HelloService {
}
//自定义MyFilter继承TypeExcludeFilter
public class MyFilter extends TypeExcludeFilter {
//如果被扫描到的bean对象的类名和HelloService类名一样,这个类就不会被装配到spring容器中
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return HelloService.class.getName().equals(metadataReader.getClassMetadata().getClassName());
}
}
//自定义spring容器的上下文对象
public class MySpringApplicationInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
//在容器初始化的时候将自定义的后置处理器加入到spring容器中
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.addBeanFactoryPostProcessor(new MyFilterPostProcessor());
}
//自定义后置处理器,将自定义的MyFilter对象装配到spring容器中
private static class MyFilterPostProcessor implements PriorityOrdered,BeanDefinitionRegistryPostProcessor{
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
beanDefinitionRegistry.registerBeanDefinition(MyFilter.class.getName(),new RootBeanDefinition(MyFilter.class));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}
}
想要上面自定义的spring容器的上下文对象生效,必须重写META-INF/spring.factories中默认的ApplicationContextInitializer对象
org.springframework.context.ApplicationContextInitializer=org.javaboy.csrf3.config.MySpringApplicationInit
@SpringBootApplication
@EnableFruit
public class Csrf3Application {
@Autowired
HelloService helloService;
public static void main(String[] args) {
SpringApplication.run(Csrf3Application.class, args);
}
}
项目启动的时候就会报错
修改一下匹配规则
public class MyFilter extends TypeExcludeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return HelloService.class.getName().equals(metadataReader.getClassMetadata().getClassName()+"aa");
}
}
项目就能正常启动。