SpringBoot中@SpringBootApplication注解详解

@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容器中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bt58xJGJ-1610940608170)(C:\Users\23746\AppData\Roaming\Typora\typora-user-images\image-20210118103857151.png)]

@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);
    }

}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3JgoT9x-1610940608176)(C:\Users\23746\AppData\Roaming\Typora\typora-user-images\image-20210118102403263.png)]

形式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 {
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9RA7fdFI-1610940608179)(C:\Users\23746\AppData\Roaming\Typora\typora-user-images\image-20210118102403263.png)]

形式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 {
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CAZ6oEVn-1610940608181)(C:\Users\23746\AppData\Roaming\Typora\typora-user-images\image-20210118102403263.png)]

形式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 {
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHs7BQYk-1610940608184)(C:\Users\23746\AppData\Roaming\Typora\typora-user-images\image-20210118102403263.png)]

我们再来看一下@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对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gvdjobyc-1610940608186)(C:\Users\23746\AppData\Roaming\Typora\typora-user-images\image-20210118112531434.png)]

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);
    }

}

项目启动的时候就会报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MPxiUWOY-1610940608187)(C:\Users\23746\AppData\Roaming\Typora\typora-user-images\image-20210118112749696.png)]

修改一下匹配规则

public class MyFilter extends TypeExcludeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return HelloService.class.getName().equals(metadataReader.getClassMetadata().getClassName()+"aa");
    }
}

项目就能正常启动。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值