当前位置: 首页>后端>正文

springboot的理解

起步依赖

    <!--springboot创建必须导入的其父类的依赖管理包-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.2.RELEASE</version>
</parent>

按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-starter-parent的pom.xml,按住Ctrl点击pom.xml中的spring-boot-starter-dependencies,跳转到了spring-boot-starter-dependencies的pom.xml,从上面的spring-boot-starter-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了(不会出现版本冲突的问题)。所以起步依赖的作用就是进行依赖的传递。

当我们导入依赖:spring-boot-starter-web

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml,spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用,同时加载tomcat,只要启动main方法,就相当于起到tomcat进行开发;同时加载json,支持springmvc的数据请求和响应。

自动配置原理解析

@SpringBootApplication
public class SpringbootDemo02Application {

    public static void main(String[] args) {
        ApplicationContext act = SpringApplication.run(SpringbootDemo02Application.class, args);
        for (String name : act.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

}

按住Ctrl点击查看启动类MySpringBootApplication上的注解注解@SpringBootApplication的源封装spring注解的复合注解有:
@ComponentScan
@SpringBootConfiguration
@EnableAutoConfiguration

@ComponentScan 解析

1.component是组件,scan是扫描,所以这个注解的含义就是用来扫描组件的
2.componentScan 扫描当前包及其子包下被 @Component,@Controller,@Service,@Repository
注解标记的类并纳入到spring容器中进行管理,所以这个注解会自动注入所有在主程序所在包下的组件。默认把当前启动类所在的包作为扫描包的起点
相当与以前的ssm扫描包:

<context:component-scan base-package="com.xxx"></context:component-scan>

@SpringBootConfiguration解释

按住Ctrl点击查看注解 @EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

从注解可以看出,这个就是标志该类是配置类@Configuration

@EnableAutoConfiguration的理解是最为重点

按住Ctrl点击查看注解 @EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

注意有两个注解:
@AutoConfigurationPackage :这个注解是自动导入我们配置的包
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

Class<?>[] exclude() default {};

String[] excludeName() default {};

}
重点理解import导入的自动选择配置类:
AutoConfigurationImportSelector.class
其流程点击进去可以看到:

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

其有一个方法selectImports(),是选择要导入的组件,其方法内:getAutoConfigurationEntry方法是获取自动配置的实体方法

   protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

getAutoConfigurationEntry方法中有getCandidateConfigurations获取候选配置的方法,具体看看是获取那些配置.点击可以看到,获取的资源配置会遍历存到 Properties properties = PropertiesLoaderUtils.loadProperties(resource)文件中,所以当我们用配置文件时,会出现提示的原因;

 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

方法内部有loadFactoryNames的方法,是获取工厂配置文件的名字,点击可以看到

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

该方法是加载我们自己配置的资源或者系统里面的资源,都是META-INF/spring.factories路径下配置的资源

 Enumeration<URL> urls = classLoader != null classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");

从下面这里也可以知道,配置文件的资源都是从META-INF/spring.factories路径加载的

Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");

loadFactoryNames方法的参数this.getSpringFactoriesLoaderFactoryClass() 点击可以看到该方法是加载EnableAutoConfiguration.class,也就是含有这个注解的类都会被导入,也就是我们的启动类(@SpringBootApplication 含有该注解),所以我们的启动类会的所以配置文件会加载进去.

 protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

再看看系统导入的配置文件META-INF/spring.factories

里面都是XXXAutoConfiguration配置文件,所以启动的时候,这些配置文件也会被加载进去

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

但是这些文件到会加载进去,为什么都没有生效呢?点击一个配置文件可以看到有@ConditionalOnClass或者@ConditionalOnXXX 这些注解,这些都是这些配置文件生效的条件:从@Configuration可以看出是一个配置文件,@Bean就是将该类的方法导入到IOC容器中.所以当我们在pom.xml导入我们需要的依赖时,响应下系统文件就会满足@ConditionalOnXXX 条件,里面组件我们就可以直接拿来使用,这就是自动装配的好处,一般系统配置组件都有默认设置;

@Configuration
@ConditionalOnClass({RabbitTemplate.class, Channel.class})
@EnableConfigurationProperties({RabbitProperties.class})
@Import({RabbitAnnotationDrivenConfiguration.class})
public class RabbitAutoConfiguration {
    public RabbitAutoConfiguration() {
    }

    @Configuration
    @ConditionalOnClass({RabbitMessagingTemplate.class})
    @ConditionalOnMissingBean({RabbitMessagingTemplate.class})
    @Import({RabbitAutoConfiguration.RabbitTemplateConfiguration.class})
    protected static class MessagingTemplateConfiguration {
        protected MessagingTemplateConfiguration() {
        }

        @Bean
        @ConditionalOnSingleCandidate(RabbitTemplate.class)
        public RabbitMessagingTemplate rabbitMessagingTemplate(RabbitTemplate rabbitTemplate) {
            return new RabbitMessagingTemplate(rabbitTemplate);
        }

配置文件application.yaml的理解

其实我们导入的依赖就是spring.factories配置文件生效的配置组件,当我们配置application.yaml配置时,其实就是自定义我们的配置文件的属性:从注解
@EnableConfigurationProperties({RedisProperties.class})
@ConditionalOnProperty(
prefix = "spring.rabbitmq.listener",
name = {"type"},
havingValue = "simple",
matchIfMissing = true
)
这些注解可以知道,我们在配置文件输入的就是在自定义我们的配置类.
且注解 @ConfigurationProperties (prefix=“配置文件中的key的前缀”)可以将配置文件中的配置自动与实体进行映射

@Configuration
@ConditionalOnClass({RabbitTemplate.class, Channel.class})
@EnableConfigurationProperties({RabbitProperties.class})
@Import({RabbitAnnotationDrivenConfiguration.class})
public class RabbitAutoConfiguration {
    public RabbitAutoConfiguration() {
    }

  @Bean(
        name = {"rabbitListenerContainerFactory"}
    )
    @ConditionalOnMissingBean(
        name = {"rabbitListenerContainerFactory"}
    )
    @ConditionalOnProperty(
        prefix = "spring.rabbitmq.listener",
        name = {"type"},
        havingValue = "simple",
        matchIfMissing = true
    )
    public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        return factory;
    }

SpringApplication的理解

主要的作用:
推断项目类型是普通类型还是web类型,要是web类型就继续保持运行状态
查找加载所有的初始化器,设置到初始化的属性中
查找监听器,设置到监听器属性中
推断并设置main方法定义的类,找到运行的类


@SpringBootApplication//springboot的入口
public class Application {

    public static void main(String[] args) {
        System.out.println("hahaha " );
        SpringApplication.run(Application.class,args);
    }

}

总结:

导入父类,可以定义好我们的依赖版本以及管理.当我们导入我们想要的依赖时,系统会根据我们的导入的依赖自动选择相应的组件.当我们要对相应的组件进行设置时,配置application.yaml配置文件即可,如果不配置,系统的组件会使用默认的设置.当然,我们也可以自定义组件,通过使用用@bean和@Configuration 注解可以自定义组件.


https://www.xamrdz.com/backend/3aw1927752.html

相关文章: