zoukankan      html  css  js  c++  java
  • SpringBoot自动配置原理

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

    2)、@EnableAutoConfiguration 作用:

    SpringFactoriesLoader.loadFactoryNames()
    扫描所有jar包类路径下 META‐INF/spring.factories
    把扫描到的这些文件的内容包装成properties对象
    从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中

     将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;

    E:DevelopToolsapache-maven-3.6.0
    epsitoryorgmybatisspringootmybatis-spring-boot-autoconfigure2.0.1mybatis-spring-boot-autoconfigure-2.0.1.jar!META-INFspring.factories
    
    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

    每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;
    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package org.mybatis.spring.boot.autoconfigure;
    
    import java.util.Iterator;
    import java.util.List;
    import java.util.function.Consumer;
    import javax.sql.DataSource;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.mapping.DatabaseIdProvider;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.session.ExecutorType;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.mapper.ClassPathMapperScanner;
    import org.mybatis.spring.mapper.MapperFactoryBean;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.ResourceLoaderAware;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.util.Assert;
    import org.springframework.util.CollectionUtils;
    import org.springframework.util.ObjectUtils;
    import org.springframework.util.StringUtils;
    
    @Configuration
    @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
    @ConditionalOnSingleCandidate(DataSource.class)
    @EnableConfigurationProperties({MybatisProperties.class})
    @AutoConfigureAfter({DataSourceAutoConfiguration.class})
    public class MybatisAutoConfiguration implements InitializingBean {
        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<ConfigurationCustomizer> configurationCustomizers;
    
        public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
            this.properties = properties;
            this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();
            this.resourceLoader = resourceLoader;
            this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();
            this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();
        }
    
        public void afterPropertiesSet() {
            this.checkConfigFileExists();
        }
    
        private void checkConfigFileExists() {
            if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
                Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
                Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
            }
    
        }
    
        @Bean
        @ConditionalOnMissingBean
        public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
            SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
            factory.setDataSource(dataSource);
            factory.setVfs(SpringBootVFS.class);
            if (StringUtils.hasText(this.properties.getConfigLocation())) {
                factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
            }
    
            this.applyConfiguration(factory);
            if (this.properties.getConfigurationProperties() != null) {
                factory.setConfigurationProperties(this.properties.getConfigurationProperties());
            }
    
            if (!ObjectUtils.isEmpty(this.interceptors)) {
                factory.setPlugins(this.interceptors);
            }
    
            if (this.databaseIdProvider != null) {
                factory.setDatabaseIdProvider(this.databaseIdProvider);
            }
    
            if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
                factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
            }
    
            if (this.properties.getTypeAliasesSuperType() != null) {
                factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
            }
    
            if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
                factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
            }
    
            if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
                factory.setMapperLocations(this.properties.resolveMapperLocations());
            }
    
            return factory.getObject();
        }
    
        private void applyConfiguration(SqlSessionFactoryBean factory) {
            org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration();
            if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
                configuration = new org.apache.ibatis.session.Configuration();
            }
    
            if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
                Iterator var3 = this.configurationCustomizers.iterator();
    
                while(var3.hasNext()) {
                    ConfigurationCustomizer customizer = (ConfigurationCustomizer)var3.next();
                    customizer.customize(configuration);
                }
            }
    
            factory.setConfiguration(configuration);
        }
    
        @Bean
        @ConditionalOnMissingBean
        public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
            ExecutorType executorType = this.properties.getExecutorType();
            return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);
        }
    
        @Configuration
        @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
        @ConditionalOnMissingBean({MapperFactoryBean.class})
        public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
            public MapperScannerRegistrarNotFoundConfiguration() {
            }
    
            public void afterPropertiesSet() {
                MybatisAutoConfiguration.logger.debug("No {} found.", MapperFactoryBean.class.getName());
            }
        }
    
        public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
            private BeanFactory beanFactory;
            private ResourceLoader resourceLoader;
    
            public AutoConfiguredMapperScannerRegistrar() {
            }
    
            public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                if (!AutoConfigurationPackages.has(this.beanFactory)) {
                    MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
                } else {
                    MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
                    List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
                    if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
                        packages.forEach((pkg) -> {
                            MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
                        });
                    }
    
                    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
                    if (this.resourceLoader != null) {
                        scanner.setResourceLoader(this.resourceLoader);
                    }
    
                    scanner.setAnnotationClass(Mapper.class);
                    scanner.registerFilters();
                    scanner.doScan(StringUtils.toStringArray(packages));
                }
            }
    
            public void setBeanFactory(BeanFactory beanFactory) {
                this.beanFactory = beanFactory;
            }
    
            public void setResourceLoader(ResourceLoader resourceLoader) {
                this.resourceLoader = resourceLoader;
            }
        }
    }
    View Code
    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package org.mybatis.spring.boot.autoconfigure;
    
    import java.io.IOException;
    import java.util.Optional;
    import java.util.Properties;
    import java.util.function.Function;
    import java.util.function.IntFunction;
    import java.util.stream.Stream;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.ExecutorType;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.context.properties.NestedConfigurationProperty;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    
    @ConfigurationProperties(
        prefix = "mybatis"
    )
    public class MybatisProperties {
        public static final String MYBATIS_PREFIX = "mybatis";
        private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        private String configLocation;
        private String[] mapperLocations;
        private String typeAliasesPackage;
        private Class<?> typeAliasesSuperType;
        private String typeHandlersPackage;
        private boolean checkConfigLocation = false;
        private ExecutorType executorType;
        private Properties configurationProperties;
        @NestedConfigurationProperty
        private Configuration configuration;
    
        public MybatisProperties() {
        }
    
        public String getConfigLocation() {
            return this.configLocation;
        }
    
        public void setConfigLocation(String configLocation) {
            this.configLocation = configLocation;
        }
    
        public String[] getMapperLocations() {
            return this.mapperLocations;
        }
    
        public void setMapperLocations(String[] mapperLocations) {
            this.mapperLocations = mapperLocations;
        }
    
        public String getTypeHandlersPackage() {
            return this.typeHandlersPackage;
        }
    
        public void setTypeHandlersPackage(String typeHandlersPackage) {
            this.typeHandlersPackage = typeHandlersPackage;
        }
    
        public String getTypeAliasesPackage() {
            return this.typeAliasesPackage;
        }
    
        public void setTypeAliasesPackage(String typeAliasesPackage) {
            this.typeAliasesPackage = typeAliasesPackage;
        }
    
        public Class<?> getTypeAliasesSuperType() {
            return this.typeAliasesSuperType;
        }
    
        public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
            this.typeAliasesSuperType = typeAliasesSuperType;
        }
    
        public boolean isCheckConfigLocation() {
            return this.checkConfigLocation;
        }
    
        public void setCheckConfigLocation(boolean checkConfigLocation) {
            this.checkConfigLocation = checkConfigLocation;
        }
    
        public ExecutorType getExecutorType() {
            return this.executorType;
        }
    
        public void setExecutorType(ExecutorType executorType) {
            this.executorType = executorType;
        }
    
        public Properties getConfigurationProperties() {
            return this.configurationProperties;
        }
    
        public void setConfigurationProperties(Properties configurationProperties) {
            this.configurationProperties = configurationProperties;
        }
    
        public Configuration getConfiguration() {
            return this.configuration;
        }
    
        public void setConfiguration(Configuration configuration) {
            this.configuration = configuration;
        }
    
        public Resource[] resolveMapperLocations() {
            return (Resource[])Stream.of((Object[])Optional.ofNullable(this.mapperLocations).orElse(new String[0])).flatMap((location) -> {
                return Stream.of(this.getResources(location));
            }).toArray((x$0) -> {
                return new Resource[x$0];
            });
        }
    
        private Resource[] getResources(String location) {
            try {
                return resourceResolver.getResources(location);
            } catch (IOException var3) {
                return new Resource[0];
            }
        }
    }
    View Code

    3)、每一个自动配置类进行自动配置功能;

    4)、以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;

    @Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
    @EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的
    ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把
    HttpEncodingProperties加入到ioc容器中
    @ConditionalOnWebApplication //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果
    满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
    @ConditionalOnClass(CharacterEncodingFilter.class) //判断当前项目有没有这个类
    CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
    @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing =
    true) //判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的
    //即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
    public class HttpEncodingAutoConfiguration {
    //他已经和SpringBoot的配置文件映射了
    private final HttpEncodingProperties properties;
    //只有一个有参构造器的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
    this.properties = properties;
    }
    @Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
    @ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件?
    public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
    return filter;
    }

    5)、所有在配置文件中能配置的属性都是在xxxxProperties类中封装者‘;配置文件能配置什么就可以参照某个功能对应的这个属性类

    @ConfigurationProperties(prefix = "spring.http.encoding") //从配置文件中获取指定的值和bean的属
    性进行绑定
    public class HttpEncodingProperties {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF‐8");

    精髓:
    1)、SpringBoot启动会加载大量的自动配置类
    2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
    3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
    4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这
    些属性的值;
    xxxxAutoConfigurartion:自动配置类;
    给容器中添加组件

    xxxxProperties:封装配置文件中相关属性;

  • 相关阅读:
    C#/JAVA 程序员转GO/GOLANG程序员笔记大全(DAY 04)
    C#/JAVA 程序员转GO/GOLANG程序员笔记大全(DAY 03)
    C#/JAVA 程序员转GO/GOLANG程序员笔记大全(DAY 02)
    C#/Java 程序员转GO/golang程序员笔记大全(day 01)
    selenium grid配置
    JUnit和TestNG的区别
    【转载】 HttpClient、HttpURLConnection、OKHttp和Volley优缺点和性能对比
    selenide总结
    docker操作
    python连接oracle数据库
  • 原文地址:https://www.cnblogs.com/ustc-anmin/p/11267089.html
Copyright © 2011-2022 走看看