SpringBoot注解@SpringBootApplication最详解

导读:本篇文章讲解 SpringBoot注解@SpringBootApplication最详解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1、@SpringBootApplication启动

1.1、@SpringBootApplication组成部分

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
  • @Target(ElementType.TYPE)
    注解的作用目标
    (ElementType.TYPE)——接口、类、枚举、注解
  • @Retention(RetentionPolicy.RUNTIME)
    注解的保留位置
    这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用
  • @Documented
    说明该注解将被包含在javadoc中
  • @Inherited
    说明子类可以继承父类中的该注解

以上四个注解非常熟悉就不再赘述,下面就重点说一下后面非常重要的三个注解

2、@SpringBootConfiguration组成

//@SpringBootConfiguration组成
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed

其中最重要的是@Configuration 注解,就这个注解来详细解释,当然我们继续深入进去

//Configuration组成
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component

我们知道Spring容器在启动的时候会默认加载一些PostProcessor(后置处理器),其中有一个处理器叫ConfigurationClassPostProcessor,这个处理器就是专门处理带有Configuration注解的类,里面有一个方法如下图:
在这里插入图片描述
我们进入这个方法中,查看源代码
在这里插入图片描述
在这里插入图片描述
在第二个循环中通过enhance()方法对所有能够找到的带Configuration注解的类进行加强,实际上替换看原来的Beanclass使用了代理类。编写代码测试一下

@Configuration
public class Myconfig {
    @Bean
    Autor getAutor(){
        return new Autor();
    }
    @Bean
    Book getBook(){
        return new Book(getAutor());
    }
}

@SpringBootTest
class Demo01ApplicationTests {
    @Autowired
    Myconfig myconfig;
    @Test
    void contextLoads() {
        System.out.println("myconfig = " + myconfig);
    }

}

结果可以得到一个CGLIB代理类
在这里插入图片描述在这里插入图片描述
然后我们再通过一段测试代码说明@Configuration和@Component区别

public class Book {
    private Autor autor;

    public Book() {
    }

    public Book(Autor autor) {
        this.autor = autor;
    }

    public Autor getAutor() {
        return autor;
    }

    public void setAutor(Autor autor) {
        this.autor = autor;
    }
}

public class Autor {
}

@Configuration
public class Myconfig {
    @Bean
    Autor getAutor(){
        return new Autor();
    }
    @Bean
    Book getBook(){
        return new Book(getAutor());
    }
}

//测试方法
@SpringBootTest
class Demo01ApplicationTests {

    @Autowired
    Autor autor;
    @Autowired
    Myconfig myconfig;
    @Autowired
    Book book;
    @Test
    void contextLoads() {
        System.out.println("myconfig = " + myconfig);
        System.out.println("book.getAutor() = " + (autor == book.getAutor()));
    }
}

测试结果:
在这里插入图片描述
接着我们将@Configuration注解替换为@Component

@Configuration
public class Myconfig {
    @Bean
    Autor getAutor(){
        return new Autor();
    }
    @Bean
    Book getBook(){
        return new Book(getAutor());
    }
}

我们看结果:
在这里插入图片描述
对比两次结果发现BOOK类第一拿到的Autor类就是我们注入容器中的那个类,而第二次拿到的类是由getAutor方法创建的新类,是因为带有@Configuration注解被替换成了CGLB代理类,内部得到进一步加强,主要加入了Bean的拦截器intercept,拦截所有带@Bean注解的方法,所以这个拦截器就会查找容器中是否存在Autor类,如果存在就不会调用getAutor方法去创建的新类,如果不存在才会创建新类。而@Component就不会去扫描容器中是否会存在Autor类,就会直接调用getAutor方法去创建的新类。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
方法isCurrentlyInvokedFactoryMethod是判断Bean中getAutor方法到底是在@Bean getAutor()中还是在@Bean getBook()中,第一次是会进入if中调用cglibMethodProxy.invokeSuper()方法调用 getAutor()方法去创建,第二次进入是调用cglibMethodProxy.invokeSuper()方法调用 getBook()方法去创建,第三次进入是不会进入if中会直接调用resolveBeanReference方法在beanFactory中去寻找,因为 getBook()方法中的getAutor()方法不是在当前Bean中的方法,已经在第一次的Bean中创建成功了
在这里插入图片描述

3、@EnableAutoConfiguration开启自动化配置

3.1、@EnableAutoConfiguration组成部分

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)

3.1.1、@Import注解

在原生的Spring Framework中,组件装配有三个阶段:

  • Spring2.5+ @Component
  • Spring3.0+ 使用@Configuration和+@Bean
  • Spring3.1+ @Enablexxx+@Import

例子:

public class Apple {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Banana {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


public class FruitImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{Apple.class.getName(), Banana.class.getName()};//返回字符串数组装着Bean的名字
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
//1.@Import({Apple.class, Banana.class})
//2.可以直接导入配置类
//3.@Import(FruitImportSelector.class)
//4.@Import(FruitImportDefinitionRegistar.class)
public @interface EableFruit {
}

@SpringBootApplication
@EableFruit
public class Demo04Application {
    public static void main(String[] args) {
        SpringApplication.run(Demo04Application.class, args);
    }

}

3.1.2、@Import(AutoConfigurationImportSelector.class)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class FruitImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{Apple.class.getName(), Banana.class.getName()};//返回字符串数组装着Bean的名字
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(FruitImportSelector.class)
public @interface EableFruit {
}

@SpringBootApplication
@EableFruit
public class Demo04Application {
    public static void main(String[] args) {
        SpringApplication.run(Demo04Application.class, args);
    }
}

作用:加载资源文件(找到资源目录)
在这里插入图片描述
META-INF/spring.factories目录中配置了启动类的位置

3.2、@AutoConfigurationPackage组成部分

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)

3.2.1、@Import(AutoConfigurationPackages.Registrar.class)

在这里插入图片描述
在这里插入图片描述

public class FruitImportDefinitionRegistar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("apple", new RootBeanDefinition(Apple.class));
        registry.registerBeanDefinition("banana", new RootBeanDefinition(Banana.class));
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(FruitImportDefinitionRegistar.class)
public @interface EableFruit {
}

@SpringBootApplication
@EableFruit
public class Demo04Application {
    public static void main(String[] args) {
        SpringApplication.run(Demo04Application.class, args);
    }
}

4、@ComponentScan包扫描

只扫描启动类所在包下的东西

//包扫描
@ComponentScan(excludeFilters//除去某些不扫描的类的过滤器 = { 
@Filter(type = FilterType.CUSTOM,
 classes = TypeExcludeFilter.class),//自定义过滤器
@Filter(type = FilterType.CUSTOM, 
classes = AutoConfigurationExcludeFilter.class)//不扫描自动化配置类
//之前的@EnableAutoConfiguration已经扫描过
 })

在这里插入图片描述
在这里插入图片描述

@Service
public class HelloService {
}

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

public class MyAppInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override//后置处理器
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(new MyTypeExcludeFilterPostProcessor());
    }
    private static class MyTypeExcludeFilterPostProcessor implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
            beanDefinitionRegistry.registerBeanDefinition(MyTypeExcludeFilter.class.getName(),new RootBeanDefinition(MyTypeExcludeFilter.class));
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

        }

        @Override
        public int getOrder() {//优先级
            return HIGHEST_PRECEDENCE;
        }
    }
}

在这里插入图片描述

org.springframework.context.ApplicationContextInitializer=com.dong.demo04.Config.MyAppInit

这样就会用自定义的过滤器过滤掉HelloService类,后置处理器将MyTypeExcludeFilterPostProcessor加载进来
而MyTypeExcludeFilterPostProcessor将MyTypeExcludeFilter注册到Bean中,这样就完成了对自定义的过滤器

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/15784.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!