SpringBoot】SpringBoot自动装配原理?

2020-12-09 16:57发布

8条回答
kitidog2016
2楼 · 2020-12-10 09:47

先看看SpringBoot的主配置类:

里面有一个main方法运行了一个run()方法,在run方法中必须要传入一个被@SpringBootApplication注解的类。

@SpringBootApplication

SpringBoot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就会运行这个类的main方法来启动SpringBoot项目。

那@SpringBootApplication注解到底是什么呢,点进去看看:

发现@SpringBootApplication是一个组合注解。

@SpringBootConfiguration

 

先看看@SpringBootConfiguration注解:

这个注解很简单,表明该类是一个Spring的配置类。

再进去看看@Configuration:

说明Spring的配置类也是Spring的一个组件。

@EnableAutoConfiguration

这个注解是开启自动配置的功能。

先看看@AutoConfigurationPackage注解:

这个注解是自动配置包,主要是使用的@Import来给Spring容器中导入一个组件 ,这里导入的是Registrar.class。

来看下这个Registrar:

就是通过这个方法获取扫描的包路径,可以debug看看:

在这行代码上打了一个断点:

启动项目:

进入断点处:

看看能否获取扫描的包路径:

已经获取到了包路径:

那那个metadata是什么呢:

可以看到是标注在@SpringBootApplication注解上的DemosbApplication,也就是我们的主配置类:

说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意。

现在包扫描路径获取到了,那具体加载哪些组件呢,看看下面这个注解。

@Import({AutoConfigurationImportSelector.class})

@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。

里面有一个selectImports方法,将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中。

debug运行看看:

会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件:

有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。

那他是如何获取到这些配置类的呢,看看上面这个方法:

会从META-INF/spring.factories中获取资源,然后通过Properties加载资源:

Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。

J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-2.0.3.RELEASE.jar:

比如看看WebMvcAutoConfiguration:

都已经帮我们配置好了,我们不用再单独配置了:


@CcCc
3楼 · 2020-12-10 15:16

先看看SpringBoot的主配置类:

里面有一个main方法运行了一个run()方法,在run方法中必须要传入一个被@SpringBootApplication注解的类。

@SpringBootApplication

SpringBoot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就会运行这个类的main方法来启动SpringBoot项目。

那@SpringBootApplication注解到底是什么呢,点进去看看:

发现@SpringBootApplication是一个组合注解。

@SpringBootConfiguration

 

先看看@SpringBootConfiguration注解:

这个注解很简单,表明该类是一个Spring的配置类。

再进去看看@Configuration:

说明Spring的配置类也是Spring的一个组件。

@EnableAutoConfiguration

这个注解是开启自动配置的功能。

先看看@AutoConfigurationPackage注解:

这个注解是自动配置包,主要是使用的@Import来给Spring容器中导入一个组件 ,这里导入的是Registrar.class。

来看下这个Registrar:

就是通过这个方法获取扫描的包路径,可以debug看看:

在这行代码上打了一个断点:

启动项目:

进入断点处:

看看能否获取扫描的包路径:

已经获取到了包路径:

那那个metadata是什么呢:

可以看到是标注在@SpringBootApplication注解上的DemosbApplication,也就是我们的主配置类:

说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意。

现在包扫描路径获取到了,那具体加载哪些组件呢,看看下面这个注解。

@Import({AutoConfigurationImportSelector.class})

@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。

里面有一个selectImports方法,将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中。

debug运行看看:

会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件:

有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。

那他是如何获取到这些配置类的呢,看看上面这个方法:

会从META-INF/spring.factories中获取资源,然后通过Properties加载资源:

Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。

J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-2.0.3.RELEASE.jar:

比如看看WebMvcAutoConfiguration:

都已经帮我们配置好了,我们不用再单独配置了:



天天
4楼 · 2020-12-12 09:26

自动配置原理

1).SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration

2).@EnableAutoConfiguration作用:


jianxiangxiong
5楼 · 2020-12-15 17:10

@Configuration

@ConditionalOnWebApplication(

    type = Type.SERVLET

)

public class ServletEndpointManagementContextConfiguration {

    public ServletEndpointManagementContextConfiguration() {

    }

 

    @Bean

    public ExposeExcludePropertyEndpointFilter servletExposeExcludePropertyEndpointFilter(WebEndpointProperties properties) {

        Exposure exposure = properties.getExposure();

        return new ExposeExcludePropertyEndpointFilter(ExposableServletEndpoint.class, exposure.getInclude(), exposure.getExclude(), new String[0]);

    }

 

    @Configuration

    @ConditionalOnClass({ResourceConfig.class})

    @ConditionalOnMissingClass({"org.springframework.web.servlet.DispatcherServlet"})

    public class JerseyServletEndpointManagementContextConfiguration {

        public JerseyServletEndpointManagementContextConfiguration() {

        }

 

        @Bean

        public ServletEndpointRegistrar servletEndpointRegistrar(WebEndpointProperties properties, ServletEndpointsSupplier servletEndpointsSupplier) {

            return new ServletEndpointRegistrar(properties.getBasePath(), servletEndpointsSupplier.getEndpoints());

        }

    }

 

    @Configuration

    @ConditionalOnClass({DispatcherServlet.class})

    public class WebMvcServletEndpointManagementContextConfiguration {

        private final ApplicationContext context;

 

        public WebMvcServletEndpointManagementContextConfiguration(ApplicationContext context) {

            this.context = context;

        }

 

        @Bean

        public ServletEndpointRegistrar servletEndpointRegistrar(WebEndpointProperties properties, ServletEndpointsSupplier servletEndpointsSupplier) {

            DispatcherServletPathProvider servletPathProvider = (DispatcherServletPathProvider)this.context.getBean(DispatcherServletPathProvider.class);

            String servletPath = servletPathProvider.getServletPath();

            if (servletPath.equals("/")) {

                servletPath = "";

            }

 

            return new ServletEndpointRegistrar(servletPath + properties.getBasePath(), servletEndpointsSupplier.getEndpoints());

        }

    }

}

自上而下观察整个类的代码,你会发现这些自动装配的套路都是一样的

1、如果当前是Servlet环境则装配这个bean

2、当存在类ResourceConfig以及不存在类DispatcherServlet时装配JerseyServletEndpointManagementContextConfiguration

3、当存在DispatcherServlet类时装配WebMvcServletEndpointManagementContextConfiguration


云鹰的指尖故事
6楼 · 2020-12-15 17:20

Spring Boot有一个全局配置文件:application.properties或application.yml。各种属性都可以在这个文件中进行配置,最常配置的比如:server.port、logging.level.* 等等,当然这些属性都可以在官方文档中查找到的https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-propertiesimage.png

接下来介绍下Spring自动配置工作原理或者叫实现方式吧

Spring Boot关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar中:

image.png

当然,自动配置原理的相关描述,官方文档貌似是没有提及。不过我们不难猜出,Spring Boot的启动类上有一个@SpringBootApplication注解,这个注解是Spring Boot项目必不可少的注解。那么自动配置原理一定和这个注解有着千丝万缕的联系!

@EnableAutoConfiguration

image.png


@SpringBootApplication是一个复合注解或派生注解,在@SpringBootApplication中有一个注解@EnableAutoConfiguration,翻译成人话就是开启自动配置,其定义如下:

而这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。

这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:

image.png
这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(...)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。

自动配置生效

每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:

image.png

以ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何生效,比如:server.port=8081,是如何生效的(当然不配置也会有默认值,这个默认值来自于org.apache.catalina.startup.Tomcat)。

image.png

在ServletWebServerFactoryAutoConfiguration类上,有一个@EnableConfigurationProperties注解:开启配置属性,而它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。


image.png


在这个类上,我们看到了一个非常熟悉的注解:@ConfigurationProperties,它的作用就是从配置文件中绑定属性到对应的bean上,而@EnableConfigurationProperties负责导入这个已经绑定了属性的bean到spring容器中(见上面截图)。那么所有其他的和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。

至此,我们大致可以了解。在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。

而诸多的XxxxAutoConfiguration自动配置类,就是Spring容器的JavaConfig形式,作用就是为Spring 容器导入bean,而所有导入的bean所需要的属性都通过xxxxProperties的bean来获得。

可能到目前为止还是有所疑惑,但面试的时候,其实远远不需要回答的这么具体,你只需要这样回答:

image.png

通过一张图标来理解一下这一繁复的流程:

image.png

总结

综上是对自动配置原理的讲解。当然,在浏览源码的时候一定要记得不要太过拘泥与代码的实现,而是应该抓住重点脉络。

一定要记得XxxxProperties类的含义是:封装配置文件中相关属性;XxxxAutoConfiguration类的含义是:自动配置类,目的是给容器中添加组件。

而其他的主方法启动,则是为了加载这些五花八门的XxxxAutoConfiguration类


你--l银河系l--我
7楼 · 2020-12-17 08:48

1)传统ssm整合redis的时候 需要在xml的配置文件中 进行大量的配置Bean

   我们在这里使用springboot来代替ssm的整合,只是通过xml的形式来整合redis

第一步:加入配置

 org.springframework.data spring-data-redis 2.0.9.RELEASE redis.clients jedis 2.9.0

第二步: 配置xml的bean的配置

 
//配置连接池                                //配置连接工厂                                            //配置 redisTemplate 模版类                                                                                                                                     

第三步:导入配置

@ImportResource(locations = "classpath:beans.xml")  可以导入xml的配置文件

@SpringBootApplication
@ImportResource(locations = "classpath:beans.xml")
@RestController
public class TulingOpenAutoconfigPrincipleApplication {

 @Autowired
 private RedisTemplate redisTemplate;

 public static void main(String[] args) {
  SpringApplication.run(TulingOpenAutoconfigPrincipleApplication.class, args);
 }

 @RequestMapping("/testRedis")
 public String testRedis() {
  redisTemplate.opsForValue().set("smlz","smlz");
  return "OK";
 }
}

 

2)综上所述 我们发现,若整合redis的时候通过传统的整合,进行了大量的配置,那么我们来看下通过springboot自动装配整合的对比

导入依赖:


   org.springframework.boot
   spring-boot-starter-data-redis
  

修改yml配置文件

spring.redis.host=47.104.128.12
spring.redis.port=6379
spring.redis.password=123456

直接使用(下述代码可以不要配置,为了解决保存使用jdk的序列方式才配置的)

    
 @Bean
 public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory)  {
  RedisTemplate template = new RedisTemplate<>();
  template.setDefaultSerializer(new Jackson2JsonRedisSerializer(Object.class));
  template.setConnectionFactory(redisConnectionFactory);
  return template;
 }

 

3)传统整合和springboot自动装配 优劣势分析。。。。。。。。。。。。

 

4)自动装配原理前的不得不说的几个注解

4.1)通过@Import注解来导入ImportSelector组件

①:写一个配置类在配置类上标注一个@Import的注解,

@Configuration
@Import(value = {TulingSelector.class})
public class TulingConfig {
}

②:在@Import注解的value值  写自己需要导入的组件 

       在selectImports方法中 就是你需要导入组件的全类名

public class TulingSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.tuling.service.TulingServiceImpl"};
    }
}

核心代码:

@RestController
public class TulingController {
    
    //自动注入 tulingServiceImpl
    @Autowired
    private TulingServiceImpl tulingServiceImpl;

    @RequestMapping("testTuling")
    public String testTuling() {
        tulingServiceImpl.testService();
        return "tulingOk";
    }
}

这里是没有标注其他注解提供给spring包扫描的
public class TulingServiceImpl {

    public void testService() {
        System.out.println("我是通过importSelector导入进来的service");
    }
}

 

1.2)通过@Import导入ImportBeanDefinitionRegistrar 从而进来导入组件

核心代码:

public class TulingImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //定义一个BeanDefinition
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TulingDao.class);
        //把自定义的bean定义导入到容器中
        beanDefinitionRegistry.registerBeanDefinition("tulingDao",rootBeanDefinition);
    }
}

通过ImportSelector功能导入进来的
public class TulingServiceImpl {

    @Autowired
    private TulingDao tulingDao;

    public void testService() {
        tulingDao.testTulingDao();
        System.out.println("我是通过importSelector导入进来的service");
    }
}

通过ImportBeanDefinitionRegistar导入进来的
public class TulingDao {

    public void testTulingDao() {
        System.out.println("我是通过ImportBeanDefinitionRegistrar导入进来tulingDao组件");
    }
}

测试结果:

1.3)spring底层条件装配的原理@Conditional

应用要求:比如我有二个组件,一个是TulingLog  一个是TulingAspect

而TulingLog 是依赖TulingAspect的 只有容器中有TulingAspect组件才会加载TulingLog

tulingLog组件  依赖TulingAspect组件
public class TulingLog {
}

tulingAspect组件
public class TulingAspect {
}

①:自定义条件组件条件

public class TulingConditional implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //容器中包含tulingAspect组件才返回Ture
        if(conditionContext.getBeanFactory().containsBean("tulingAspect")){
            return true;
        }else{
            return false;
        }

    }
}
    
    -------------------------------------该情况下会加载二个组件-------------------------------------------------

    @Bean
    public TulingAspect tulingAspect() {
        System.out.println("TulingAspect组件自动装配到容器中");
        return new TulingAspect();
    }
    
    
    @Bean
    @Conditional(value = TulingConditional.class)
    public TulingLog tulingLog() {
        System.out.println("TulingLog组件自动装配到容器中");
        return new TulingLog();
    }
    
    -------------------------------------二个组件都不会被加载----------------------------------------
    /*@Bean**/
    public TulingAspect tulingAspect() {
        System.out.println("TulingAspect组件自动装配到容器中");
        return new TulingAspect();
    }
    
    
    @Bean
    @Conditional(value = TulingConditional.class)
    public TulingLog tulingLog() {
        System.out.println("TulingLog组件自动装配到容器中");
        return new TulingLog();
    }

 

自动装配原理分析  从@SpringbootApplication入手分析

fe96adc75b7c41c952f61e4ec38fe701b31.jpg

那我们仔细分析

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports

public class AutoConfigurationImportSelector
  implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
  BeanFactoryAware, EnvironmentAware, Ordered {


 @Override
 public String[] selectImports(AnnotationMetadata annotationMetadata) {
  if (!isEnabled(annotationMetadata)) {
   return NO_IMPORTS;
  }
  AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    .loadMetadata(this.beanClassLoader);
  AnnotationAttributes attributes = getAttributes(annotationMetadata);
  //去mata-info/spring.factories文件中 查询 EnableAutoConfiguration对于值
  List configurations = getCandidateConfigurations(annotationMetadata,
    attributes);
  //去除重复的配置类,若我们自己写的starter 可能存主重复的
  configurations = removeDuplicates(configurations);
  Set exclusions = getExclusions(annotationMetadata, attributes);
  checkExcludedClasses(configurations, exclusions);
  configurations.removeAll(exclusions);
  //根据maven 导入的启动器过滤出 需要导入的配置类
  configurations = filter(configurations, autoConfigurationMetadata);
  fireAutoConfigurationImportEvents(configurations, exclusions);
  return StringUtils.toStringArray(configurations);
 }
} 
 
 //去spring.factories 中去查询EnableAutoConfirution类
 private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
  MultiValueMap result = cache.get(classLoader);
  if (result != null) {
   return result;
  }

  try {
   Enumeration urls = (classLoader != null ?
     classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
   result = new LinkedMultiValueMap<>();
   while (urls.hasMoreElements()) {
    URL url = urls.nextElement();
    UrlResource resource = new UrlResource(url);
    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    for (Map.Entry entry : properties.entrySet()) {
     List factoryClassNames = Arrays.asList(
       StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
     result.addAll((String) entry.getKey(), factoryClassNames);
    }
   }
   cache.put(classLoader, result);
   return result;
  }
  catch (IOException ex) {
   throw new IllegalArgumentException("Unable to load factories from location [" +
     FACTORIES_RESOURCE_LOCATION + "]", ex);
  }
 }

然后我们分析RedisAutoConfiguration类

     导入了三个组件  RedisTemplate   StringRedisTemplate

     JedisConnectionConfiguration 

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    
    //导入redisTemplate 
 @Bean
 @ConditionalOnMissingBean(name = "redisTemplate")
 public RedisTemplate redisTemplate(
   RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
  RedisTemplate template = new RedisTemplate<>();
  template.setConnectionFactory(redisConnectionFactory);
  return template;
 }

 @Bean
 @ConditionalOnMissingBean
 public StringRedisTemplate stringRedisTemplate(
   RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
  StringRedisTemplate template = new StringRedisTemplate();
  template.setConnectionFactory(redisConnectionFactory);
  return template;
 }

}

=====================================JedisConnectionConfiguration==========================================

@Configuration
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
class JedisConnectionConfiguration extends RedisConnectionConfiguration {

 private final RedisProperties properties;

 private final List builderCustomizers;

 JedisConnectionConfiguration(RedisProperties properties,
   ObjectProvider sentinelConfiguration,
   ObjectProvider clusterConfiguration,
   ObjectProvider> builderCustomizers) {
  super(properties, sentinelConfiguration, clusterConfiguration);
  this.properties = properties;
  this.builderCustomizers = builderCustomizers
    .getIfAvailable(Collections::emptyList);
 }

 @Bean
 @ConditionalOnMissingBean(RedisConnectionFactory.class)
 public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
  return createJedisConnectionFactory();
 }

 private JedisConnectionFactory createJedisConnectionFactory() {
  JedisClientConfiguration clientConfiguration = getJedisClientConfiguration();
  if (getSentinelConfig() != null) {
   return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration);
  }
  if (getClusterConfiguration() != null) {
   return new JedisConnectionFactory(getClusterConfiguration(),
     clientConfiguration);
  }
  return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration);
 }

 private JedisClientConfiguration getJedisClientConfiguration() {
  JedisClientConfigurationBuilder builder = applyProperties(
    JedisClientConfiguration.builder());
  RedisProperties.Pool pool = this.properties.getJedis().getPool();
  if (pool != null) {
   applyPooling(pool, builder);
  }
  if (StringUtils.hasText(this.properties.getUrl())) {
   customizeConfigurationFromUrl(builder);
  }
  customize(builder);
  return builder.build();
 }

 private JedisClientConfigurationBuilder applyProperties(
   JedisClientConfigurationBuilder builder) {
  if (this.properties.isSsl()) {
   builder.useSsl();
  }
  if (this.properties.getTimeout() != null) {
   Duration timeout = this.properties.getTimeout();
   builder.readTimeout(timeout).connectTimeout(timeout);
  }
  return builder;
 }

 private void applyPooling(RedisProperties.Pool pool,
   JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
  builder.usePooling().poolConfig(jedisPoolConfig(pool));
 }

 private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) {
  JedisPoolConfig config = new JedisPoolConfig();
  config.setMaxTotal(pool.getMaxActive());
  config.setMaxIdle(pool.getMaxIdle());
  config.setMinIdle(pool.getMinIdle());
  if (pool.getMaxWait() != null) {
   config.setMaxWaitMillis(pool.getMaxWait().toMillis());
  }
  return config;
 }
}


studentaaa
8楼 · 2020-12-20 10:06

(1)引导类上开启@duEnableAutoConfiguration

(2)dao部通zhuan过@import注解引入ImporttSelector

(3)查找工程jar包中META-INF/spring.factories文件

(4)装载shu部的对象到容器

喵喵咪
9楼 · 2020-12-31 15:18

先看看SpringBoot的主配置类:

里面有一个main方法运行了一个run()方法,在run方法中必须要传入一个被@SpringBootApplication注解的类。

@SpringBootApplication

SpringBoot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就会运行这个类的main方法来启动SpringBoot项目。

那@SpringBootApplication注解到底是什么呢,点进去看看:


相关问题推荐

  • 回答 3

    最近看了一些spring书籍,主要都是工作需要,实话说,没有必要买这么多书,每个主题一本就足够了,其他的补充可以通过项目实战和上网看官网或者博客补充。说是推荐,其实只是一些简单读后感想而已,每本书都有它的价值,即使有些写得不好,也很难否定作者的努...

  • 回答 2

            org.springframework.boot         spring-boot-starter-parent         1.3.2.RELEASE                             2.10.4         1.6.2                       ...

  • 回答 1
    已采纳

    spring-boot-starter-web 嵌入tomcat和web开发需要servlet与jsp支持spring-boot-starter-data-jpa 数据库支持spring-boot-starter-data-redis redis数据库支持spring-boot-starter-data-solr solr支持mybatis-spring-boot-starter 第三方的myba......

  • 回答 1
    已采纳

    应该说是过时了。我们起初的微服务架构是符合当时的情况的,也解决了当时的性能问题还有目的地之间孤立实现。尽管如此,我们没有准备好服务激增的改变准备。当需要批量更新时,我们缺乏适当的工具来测试和部署微服务。结果就是,我们的研发效率因此出现了滑坡...

  • 回答 10
    已采纳

    springboot的学习思路是首先掌握spring和springmvc,有了这两个框架作为基础,springboot的学习是非常简单的。springboot是spring族系中具有革命性变革的一门技术,springboot的主要设计目的是为了让开发者快速构建spirng环境,并且封装了大量的模板化配置。让...

没有解决我的问题,去提问