zoukankan      html  css  js  c++  java
  • springboot源码学习2 自动装配原理学习

    一 构建基础项目

      1 构建依赖:   

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.cxy</groupId>
        <artifactId>register-sever</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>register-sever</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

      2 查看启动类:  

    @SpringBootApplication
    //@Import({ImportTest.class, SelfImportSelector.class, SelfImportBeanDefinitionRegistrar.class})
    public class RegisterSeverApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RegisterSeverApplication.class, args);
        }
    
    }

      3 编写测试接口:

     先忽略注释的代码 

    @RestController
    public class Rest {
    /*    @Autowired
        ImportTest importTest;
        @Autowired
        ImportTestC importTestC;
        @Autowired
        ImportTestD importTestD;*/
        @RequestMapping("/hello")
        public String get(){
           // importTest.test();
           // importTestC.test();
          //  importTestD.test();
            return "hello";
        }
    }

      4 启动main方法

      调用http://localhost:8080/hello  获取结果为hello  就可以了  很多在想直接源码上不就好了么,其实可以看到代码中有很多注释的东西,这个就是为啥要这么做的原因,下面会继续讲

    二  源码跟踪

      1构造方法

        public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            // resourceLoader == null
            this.resourceLoader = resourceLoader;
    
            Assert.notNull(primarySources, "PrimarySources must not be null");
            //primarySources 为基础类
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            // 设置初始化器
            setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
            // 设置监听器
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            this.mainApplicationClass = deduceMainApplicationClass();
        }

       2 入口

     所有的方法入口都是main方法所以我们可以跟进main方法进去:

        public ConfigurableApplicationContext run(String... args) {
            // 创建计时器
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            // 设置handlerless为true
            configureHeadlessProperty();
            // 获取监听器 启动
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // 预处理环境信息
                ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                // 设置configureIgnoreBeanInfo 为true
                configureIgnoreBeanInfo(environment);
                // 打印banner
                Banner printedBanner = printBanner(environment);
                // 创建上下文ConfigurableApplicationContext
                context = createApplicationContext();
                // 获取异常报信息
                exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                // 预处理context
                prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // 刷新context  调用spring得refresh方法
                refreshContext(context);
                // 空实现
                afterRefresh(context, applicationArguments);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                }
                // 启动监听器
                listeners.started(context);
                // 执行命令行操作
                callRunners(context, applicationArguments);
            }
            catch (Throwable ex) {
                // 异常处理,打印异常报告
                handleRunFailure(context, ex, exceptionReporters, listeners);
                throw new IllegalStateException(ex);
            }
    
            try {
                // 启动监听器
                listeners.running(context);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, null);
                throw new IllegalStateException(ex);
            }
            return context;
        }

     

      3 getRunListeners(args)方法

    是获取 META-INF/spring.factories中配置的内容核心方法,配置的监听器  内容如下
        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            // 获取类加载器
            ClassLoader classLoader = getClassLoader();
            // Use names and ensure unique to protect against duplicates
            // 通过传入参数的class类型,来获取具体内容的集合  获取listener时候  传入 SpringApplicationRunListener
            Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            // 创建实例
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }

      4 prepareEnvironment(listeners, applicationArguments)方法

        这个方法是进行环境的预处理,就是激活相关配置文件,例如**.yml  核心方法 configureEnvironment

    
    
    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    if (this.addConversionService) {
    ConversionService conversionService = ApplicationConversionService.getSharedInstance();
    environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    // 处理配置文件
    configurePropertySources(environment, args);
    // 激活
    configureProfiles(environment, args);
    }

      5 createApplicationContext方法

        protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            //根据类型进行判断加载哪个ConfigurableApplicationContext
            if (contextClass == null) {
                try {
                    switch (this.webApplicationType) {
                    case SERVLET:
                        // servlet
                        contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                        break;
                        // REACTIVE
                    case REACTIVE:
                        contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                        break;
                    default:
                        // 普通main方法
                        contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                    }
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
        }

      6  getSpringFactoriesInstances方法处理实例

        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            // 获取类加载器
            ClassLoader classLoader = getClassLoader();
            // Use names and ensure unique to protect against duplicates
            // 通过传入参数的class类型,来获取具体内容的集合  获取listener时候  传入 SpringApplicationRunListener
            Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            // 创建实例
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }

      7 prepareContext预处理方法

        private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
            context.setEnvironment(environment);
            // 对容器得一个后处理 beanNameGenerator resourceLoader ConversionService
            postProcessApplicationContext(context);
            // 执行初始化器
            applyInitializers(context);
            listeners.contextPrepared(context);
            if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                // 打印profile信息
                logStartupProfileInfo(context);
            }
            // Add boot specific singleton beans
            //获取beanfactory  后续到beanfactory进行必要设置
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    
            // 封装参数注册到获取beanfactory
            beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
            if (printedBanner != null) {
                //如果beanfactory存在  注册单例bean
                beanFactory.registerSingleton("springBootBanner", printedBanner);
            }
            if (beanFactory instanceof DefaultListableBeanFactory) {
                // 设置bean对象是否可以重载
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
            if (this.lazyInitialization) {
                // 懒加载
                context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
            }
            // Load the sources
            // 找到所有得配置源
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            // 进入load方法
            load(context, sources.toArray(new Object[0]));
            // 触发contextLoaded的完成
            listeners.contextLoaded(context);
        }

      8 refreshContext

    这个方法是最复杂的方法,会加载AutoConfigurationImportSelector内容 会调用spring的refresh方法  最终解析配置文件的方法在processImports方法中

        for (SourceClass candidate : importCandidates) {
                        if (candidate.isAssignable(ImportSelector.class)) {
                            // Candidate class is an ImportSelector -> delegate to it to determine imports
                            Class<?> candidateClass = candidate.loadClass();
                            ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                    this.environment, this.resourceLoader, this.registry);
                            Predicate<String> selectorFilter = selector.getExclusionFilter();
                            if (selectorFilter != null) {
                                exclusionFilter = exclusionFilter.or(selectorFilter);
                            }
                   // 这一步是加载自动配置的关键 获取元数据加载
    if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } }
    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
            ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered 

    可以看到EnableAutoConfiguration 注解上类的加载

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        /**
         * Exclude specific auto-configuration classes such that they will never be applied.
         * @return the classes to exclude
         */
        Class<?>[] exclude() default {};
    
        /**
         * Exclude specific auto-configuration class names such that they will never be
         * applied.
         * @return the class names to exclude
         * @since 1.3.0
         */
        String[] excludeName() default {};
    
    }

    AutoConfigurationImportSelector中核心方法:

        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }

    实现了这个方法就会被加载到springboot

      9 handleRunFailure 异常处理方法

        private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
                Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
            try {
                try {
                    // 处理退出code
                    handleExitCode(context, exception);
                    if (listeners != null) {
                        listeners.failed(context, exception);
                    }
                }
                finally {
                    // 报告异常
                    reportFailure(exceptionReporters, exception);
                    if (context != null) {
                        context.close();
                    }
                }
            }
            catch (Exception ex) {
                logger.warn("Unable to close ApplicationContext", ex);
            }
            // 通过反射  报告具体类型
            ReflectionUtils.rethrowRuntimeException(exception);
        }

    三 测试

      1 新建三个类:

    public class ImportTest {
        public void test(){
            System.out.println("我是中国人");
        }
    
    }
    public class ImportTestC {
        public void test(){
            System.out.println("测试testc");
        }
    }
    public class ImportTestD {
        public void test(){
            System.out.println("测试testD");
        }
    }
    public class SelfImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            return new String[]{"com.cxy.registersever.study.ImportTestC"};
        }
    }
    public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            RootBeanDefinition root = new RootBeanDefinition(ImportTestD.class);
            registry.registerBeanDefinition("importTestD", root);
        }
    }

    2 引入:

    @SpringBootApplication
    @Import({ImportTest.class, SelfImportSelector.class, SelfImportBeanDefinitionRegistrar.class})
    public class RegisterSeverApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RegisterSeverApplication.class, args);
        }
    
    }

    3 测试:

    @RestController
    public class Rest {
        @Autowired
        ImportTest importTest;
        @Autowired
        ImportTestC importTestC;
        @Autowired
        ImportTestD importTestD;
        @RequestMapping("/hello")
        public String get(){
            importTest.test();
            importTestC.test();
            importTestD.test();
            return "hello";
        }
    }

    idea可能会报importTestC ,importTestD找不到,但是不影响,执行方法控制台会打印相关内容



      





  • 相关阅读:
    《Programming WPF》翻译 第6章 3.二进制资源
    《Programming WPF》翻译 第5章 前言
    谈谈粒子系统
    GameFramework的初步设计
    引擎层次化设计
    关于HeapCreate(1)
    新的引擎ZeusEngine
    最短路径算法介绍
    Max导出插件
    游戏中的光影效果
  • 原文地址:https://www.cnblogs.com/cxyxiaobao/p/15410042.html
Copyright © 2011-2022 走看看