fscanf返回值被忽略怎么解决_Spring Boot 的自动配置这么牛,到底是怎么做到?

作者:祖大帅
链接:juejin.im/post/5b679fbc5188251aad213110

SpringBoot 的故事从一个面试题开始

Spring Boot、Spring MVC 和 Spring 有什么区别?

d0427297d5aaf4d010abbace8be4106f.png

先来个 SpringBoot 的启动结构图

分开描述各自的特征

Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的ioc和 aop,ioc 提供了依赖注入的容器, aop解决了面向横切面的编程,然后在此两者的基础上实现了其他延伸产品的高级功能。

Spring MVC提供了一种轻度耦合的方式来开发web应用。它是Spring的一个模块,是一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。

Spring Boot实现了自动配置,降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box)。

所以,用最简练的语言概括就是

.Spring 是一个“引擎”;  .Spring MVC 是基于Spring的一个 MVC 框架;  .Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。

63e8d356d8fc6a714a77e7f0ecb05b99.png

SpringBoot到底是怎么做到自动配置的?

从代码里看项目SpringBoot的项目启动类只有一个注解@SpringBootApplication和一个run方法。

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

直接看@SpringBootApplication的代码:

@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Inherited  
@SpringBootConfiguration  
@EnableAutoConfiguration  
@ComponentScan(  
    excludeFilters = {@Filter(  
    type = FilterType.CUSTOM,  
    classes = {TypeExcludeFilter.class}  
), @Filter(  
    type = FilterType.CUSTOM,  
    classes = {AutoConfigurationExcludeFilter.class}  
)}  
)  
public @interface SpringBootApplication {  
    @AliasFor(  
        annotation = EnableAutoConfiguration.class  
    )  
    Class>\[\] exclude() default {};  

    @AliasFor(  
        annotation = EnableAutoConfiguration.class  
    )  
    String\[\] excludeName() default {};  

    @AliasFor(  
        annotation = ComponentScan.class,  
        attribute = "basePackages"  
    )  
    String\[\] scanBasePackages() default {};  

    @AliasFor(  
        annotation = ComponentScan.class,  
        attribute = "basePackageClasses"  
    )  
    Class>\[\] scanBasePackageClasses() default {};  
}

@SpringBootApplication:包含了@SpringBootConfiguration(打开是@Configuration),@EnableAutoConfiguration,@ComponentScan注解。

@Configuration

JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。 
对比一下传统XML方式和config配置方式的区别:

XML声明和定义配置方式

<?xml  version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"  xsi:schemaLocation="http://www.springframework.org/schema/beans  
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
            http://www.springframework.org/schema/aop  
            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
            http://www.springframework.org/schema/context  
            http://www.springframework.org/schema/context/spring-context-3.0.xsd  
            http://www.springframework.org/schema/tx  
            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
  ">  
  <bean id="app" class="com..." />

用一个过滤器举例,JavaConfig的配置方式是这样

@Configuration  
public class DruidConfiguration {  
    @Bean  
    public FilterRegistrationBean statFilter(){  
        //创建过滤器  
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());  
        //设置过滤器过滤路径  
        filterRegistrationBean.addUrlPatterns("/*");  
        //忽略过滤的形式  
        filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");  
        return filterRegistrationBean;  
    }  
}

任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。 任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。

@ComponentScan

@ComponentScan对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。 


我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。 注:所以SpringBoot的启动类最好是放在rootpackage下,因为默认不指定basePackages。

@EnableAutoConfiguration

(核心内容)看英文意思就是自动配置,概括一下就是,借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器。

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

里面最关键的是@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。该配置模块的主要使用到了SpringFactoriesLoader。

SpringFactoriesLoader详解

SpringFactoriesLoader为Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为factoryClass和classLoader即需要传入工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories文件,传入的工厂类为接口,而文件中对应的类则是接口的实现类,或最终作为实现类。

public abstract class SpringFactoriesLoader {  
    //...  
    public static  List loadFactories(Class<T> factoryClass, ClassLoader classLoader) {  
        ...  
    }  public static List loadFactoryNames(Class> factoryClass, ClassLoader classLoader) {  
        ....  
    }  
}

所以文件中一般为如下图这种一对多的类名集合,获取到这些实现类的类名后,loadFactoryNames方法返回类名集合,方法调用方得到这些集合后,再通过反射获取这些类的类对象、构造方法,最终生成实例。

fc03cc4b37c44d33906e97dac9ba96dd.png

下图有助于我们形象理解自动配置流程(盗个图)
d28b02c39e2b5f9efe421ea64274fb51.png

AutoConfigurationImportSelector

继续上面讲的AutoConfigurationImportSelector.class。该类主要关注selectImports方法

public String\[\] selectImports(AnnotationMetadata annotationMetadata) {  
        if (!this.isEnabled(annotationMetadata)) {  
            return NO_IMPORTS;  
        } else {  
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);  
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);  
            List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);  
            configurations = this.removeDuplicates(configurations);  
            Set exclusions = this.getExclusions(annotationMetadata, attributes);  this.checkExcludedClasses(configurations, exclusions);  
            configurations.removeAll(exclusions);  
            configurations = this.filter(configurations, autoConfigurationMetadata);  this.fireAutoConfigurationImportEvents(configurations, exclusions);  return StringUtils.toStringArray(configurations);  
        }  
    }

该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表。如果获取到类信息,spring可以通过类加载器将类加载到jvm中,现在我们已经通过spring-boot的starter依赖方式依赖了我们需要的组件,那么这些组件的类信息在select方法中就可以被获取到。

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

方法中的getCandidateConfigurations方法,其返回一个自动配置类的类名列表,方法调用了loadFactoryNames方法,查看该方法

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

自动配置器会跟根据传入的factoryClass.getName()到项目系统路径下所有的spring.factories文件中找到相应的key,从而加载里面的类。我们就选取这个mybatis-spring-boot-autoconfigure下的spring.factories文件

# Auto Configure  
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\  
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

进入org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中,又是一堆注解

@org.springframework.context.annotation.Configuration  
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})  
@ConditionalOnBean({DataSource.class})  
@EnableConfigurationProperties({MybatisProperties.class})  
@AutoConfigureAfter({DataSourceAutoConfiguration.class})  
public class MybatisAutoConfiguration  
{  
  private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);  
  private final MybatisProperties properties;  
  private final Interceptor\[\] interceptors;  
  private final ResourceLoader resourceLoader;  
  private final DatabaseIdProvider databaseIdProvider;  
  private final List configurationCustomizers;

.@Spring的Configuration是一个通过注解标注的springBean,.@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class})这个注解的意思是:当存在SqlSessionFactory.class, SqlSessionFactoryBean.class这两个类时才解析MybatisAutoConfiguration配置类,否则不解析这一个配置类。

我们需要mybatis为我们返回会话对象,就必须有会话工厂相关类.@CondtionalOnBean(DataSource.class):只有处理已经被声明为bean的dataSource.@ConditionalOnMissingBean(MapperFactoryBean.class)这个注解的意思是如果容器中不存在name指定的bean则创建bean注入,否则不执行以上配置可以保证sqlSessionFactory、sqlSessionTemplate、dataSource等mybatis所需的组件均可被自动配置,@Configuration注解已经提供了Spring的上下文环境,所以以上组件的配置方式与Spring启动时通过mybatis.xml文件进行配置起到一个效果。

只要一个基于SpringBoot项目的类路径下存在SqlSessionFactory.class, SqlSessionFactoryBean.class,并且容器中已经注册了dataSourceBean,就可以触发自动化配置,意思说我们只要在maven的项目中加入了mybatis所需要的若干依赖,就可以触发自动配置,但引入mybatis原生依赖的话,每集成一个功能都要去修改其自动化配置类,那就得不到开箱即用的效果了。

所以Spring-boot为我们提供了统一的starter可以直接配置好相关的类,触发自动配置所需的依赖(mybatis)如下:

<dependency>  
            <groupId>org.mybatis.spring.bootgroupId>  
            <artifactId>mybatis-spring-boot-starterartifactId>  
dependency>

因为maven依赖的传递性,我们只要依赖starter就可以依赖到所有需要自动配置的类,实现开箱即用的功能。也体现出Springboot简化了Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发。

-End-

加小编微信:xiaobaito,免费获取一份架构师资料。还可以邀请加入咱们的菜鸟架构技术群一起讨论技术,禁止发广告及垃圾信息哦。

热门阅读

免费获取一份架构资料!

Nginx为什么快到根本停不下来?

Spring中经典的9种设计模式,要记住啊!

微盟七天七夜找回删库数据 

GitHub 50000+ Star 登顶,命令行艺术! 

除了负载均衡,Nginx 能做的真强大! 

如何搭建自己的技术架构,说得太好了! 

被问懵了,什么是红黑树? 

更多请关注“菜鸟架构”公众号,将不断呈现更多架构干货!

7e376cab1ae04a5cbcd8dcde30c16891.png

给个在看,谢谢老板!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值