hi,你好!欢迎访问本站!登录
本站由网站地图腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

源码进修系列之SpringBoot自动设置(篇一)

2019-11-18杂谈搜奇网22°c
A+ A-

源码进修系列之SpringBoot自动设置源码进修(篇一)

ok,本博客尝试跟一下Springboot的自动设置源码,做一下笔记纪录,自动设置是Springboot的一个很症结的特征,也轻易被疏忽的属性,由于这个属性被包含在@SpringBootApplication注解里,所以不去跟一下源码都不晓得另有这个属性,ps:本博客源码基于SpringBoot1.5.7版本

@SpringBootApplication

ok,跟一下@SpringBootApplication,发明@SpringBootApplication实际上是一个复合的注解,由许多注解组成,@EnableAutoConfiguration实在只是其一部份,@EnableAutoConfiguration就是开启自动设置的注解

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@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 {
    ....
    Class<?>[] scanBasePackageClasses() default {};
}

@EnableAutoConfiguration

点进@EnableAutoConfiguration,比较重要的列出来:

  • @AutoConfigurationPackage
  • @Import({EnableAutoConfigurationImportSelector.class})
package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

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

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

    String[] excludeName() default {};
}

@AutoConfigurationPackage

先看@AutoConfigurationPackage源码,这个注解是开启自动设置包的,关注点在@Import({Registrar.class}),中心在Registrar类

备注:@import注解是Spring的底层注解,作用是导入一个组件到容器里


package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

Registrar 类是AutoConfigurationPackages类的静态内部类,

 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
        }
    }

看一下(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()猎取的是什么,用idea的东西,盘算表达式,能够看到实在猎取的是SpringBoot启动类上面的包名


AnnotationMetadata:SpringBoot注解元数据

所以,@AutoConfigurationPackage开启后,就能够将主设置类(@SpringBootApplication)地点包及其子包内里的一切组件都扫描到Spring容器里

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
        if (registry.containsBeanDefinition(BEAN)) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
            ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
            constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
        } else {// Spring容器里没有找到对应组件
            /* 将组件注册到Spring容器里 */
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();                  beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
            beanDefinition.setRole(2);
            registry.registerBeanDefinition(BEAN, beanDefinition);
        }

    }

ok,跟了源码,固然只是简朴跟一下,没有迥殊细的跟,这个历程能够看出这里的组件扫描只是扫描主设置类(@SpringBootApplication)地点包及其子包内里的一切组件,所以,写了例子考证一下:
在Application类包外写个Controller测试类

@RestController
public class TestController {

    @GetMapping("/hello")
    public String hello(){
        return "hello world!";
    }
}

启动项目,举行接见,发明都是404找不到这个接口,再将这个Controller放在包内里,才能够扫描到

@Import({EnableAutoConfigurationImportSelector.class})

package org.springframework.boot.autoconfigure;

import org.springframework.core.type.AnnotationMetadata;

/** @deprecated */
@Deprecated
public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
    public EnableAutoConfigurationImportSelector() {
    }

    protected boolean isEnabled(AnnotationMetadata metadata) {
        return this.getClass().equals(EnableAutoConfigurationImportSelector.class) ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
    }
}

重要看一下其基类AutoConfigurationImportSelector代码,看一下selectImport要领:

/**装载许多自动设置类,以全类名的体式格局返回一个字符数组**/
public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            try {
                //猎取自动设置的元数据
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                //装载设置属性
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                //经由过程类加载器读取一切的设置类全类名
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                configurations = this.sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return (String[])configurations.toArray(new String[configurations.size()]);
            } catch (IOException var6) {
                throw new IllegalStateException(var6);
            }
        }
    }
  • AutoConfigurationMetadata信息,猎取全部JavaEE系统的一些设置类,固然是Springboot集成的,比方有WebMvcAutoConfiguration自动设置类

跟一下getCandidateConfigurations要领,SpringFactoriesLoader是Spring-code工程的工场加载类,运用SpringFactoriesLoader,需要在模块的META-INF/spring.factories文件本身设置属性,这个Properties花样的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分开的完成类,运用SpringFactoriesLoader能够完成将响应的完成类注入Spirng容器

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 final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    /* 将spring.factories的类都装载到Spring容器*/
     public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();

        try {
        //将META-INF/spring.factories文件里设置的属性都装载到Enumeration数据结构里
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            ArrayList result = new ArrayList();
            //遍历猎取属性,然后再猎取对应的设置类全类名
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }

            return result;
        } catch (IOException var8) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
        }
    }

然后META-INF/spring.factories文件放在哪?很显然是放在Springboot的自动设置模块里,如图:

所以,@Import({EnableAutoConfigurationImportSelector.class})开启以后,重如果EnableAutoConfigurationImportSelector这个类的作用就是在SpringBoot启动时刻将从SpringBoot自动设置工程的META-INF/spring.factories文件中猎取指定的值,经由SpringFactoriesLoader加载以后将许多自动设置类加载到Spring容器,所以我们不需要设置,mvc等等默许设置就已跟着SpringBoot启动而自动见效

ok,Springboot的自动设置类都在这个包里,源码许多,所以本博客只是简朴跟一下源码

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
源码进修系列之SpringBoot自动设置(篇一)

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/282091.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>