分布式微服务架构之SpringBoot定制篇

   日期:2020-10-08     浏览:85    评论:0    
核心提示:一,springboot的启动原理@SpringBootApplicationpublic class Application { public static void main(String[] args) { //从主程序的入口进入 SpringApplication.run(Application.class,args); }} public static ConfigurableApplicationContext run(Class<

一,springboot的启动原理

@SpringBootApplication
public class Application { 
    public static void main(String[] args) { 
    	//从主程序的入口进入
        SpringApplication.run(Application.class,args);
    }
}
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { 
        //这里的流程实际上分为了两步:
        //第一步:创建SpringApplication
        //第二步:执行run()
        return new SpringApplication(primarySources).run(args);
    }

1.创建SpringApplication

进入new SpringApplication()

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 
        //保存主配置类
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //判断当前是否是一个web应用
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //从类路径下找到META-INF/spring.factory配置的所有ApplicationContextInitializer,然后保存起来 TODO
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //从类路径下找到META-INF/spring.factory配置的所有ApplicationListener,然后保存起来
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //从多个配置类找到main方法的主配置类
        this.mainApplicationClass = deduceMainApplicationClass();
    }

再来看getSpringFactoriesInstances()

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { 
        ClassLoader classLoader = getClassLoader();
        //SpringFactoriesLoader.loadFactoryNames(type, classLoader)
        //往工厂里面加载bean
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

接着看loadFactoryNames()

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { 
        String factoryTypeName = factoryType.getName();
        //调用了loadSpringFactories
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { 
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) { 
            return result;
        }

        try { 
            //从FACTORIES_RESOURCE_LOCATION下加载配置文件
            //FACTORIES_RESOURCE_LOCATION代表的哪里?
            //public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
            Enumeration<URL> 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()) { 
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { 
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) { 
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

再回到SpringApplication()看setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { 
        //实际调用的是getSpringFactoriesInstances
        return getSpringFactoriesInstances(type, new Class<?>[] { });
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { 
        ClassLoader classLoader = getClassLoader();
        //调用了 loadFactoryNames
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

这里就和上一步其实是一样的,从META-INF/spring.factories加载bean。

2.执行run()

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { 
		return new SpringApplication(primarySources).run(args);
	}

点击进入run()

    public ConfigurableApplicationContext run(String... args) { 
        //监听springboot应用的创建
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        //获取SpringApplicationRunListeners;从类路径下META‐INF/spring.factories
        SpringApplicationRunListeners listeners = getRunListeners(args);
        //回调所有的获取SpringApplicationRunListener.starting()方法
        listeners.starting();
        try { 
            //封装命令行参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //准备环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成 
            Banner printedBanner = printBanner(environment);
            //创建ApplicationContext;决定创建web的ioc还是普通的ioc 利用反射创建
            context = createApplicationContext();
            //做异常打印报告的,没啥用
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] {  ConfigurableApplicationContext.class }, context);
            //准备上下文环境;将environment保存到ioc中;而且applyInitializers();
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //容器刷新,ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat)
            refreshContext(context);
            //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
            //ApplicationRunner先回调,CommandLineRunner再回调
            afterRefresh(context, applicationArguments);
            //停止监听
            stopWatch.stop();
            if (this.logStartupInfo) { 
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            //所有的SpringApplicationRunListener回调finished方法
            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;
    }

prepareContext()

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { 
		//给上下文设置环境
		context.setEnvironment(environment);
		//执行上下文的后置处理器,给容器中加载一些组件
		postProcessApplicationContext(context);
		//回调之前保存的所有的ApplicationContextInitializer的initialize方法
		applyInitializers(context);
		//回调所有的SpringApplicationRunListener的contextPrepared();
		listeners.contextPrepared(context);
		if (this.logStartupInfo) { 
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) { 
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) { 
			((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(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}

refresh()

	public void refresh() throws BeansException, IllegalStateException { 
		synchronized (this.startupShutdownMonitor) { 
			// 清空缓存
			prepareRefresh();

			// cas的方式初始化bean工厂
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 创建bean工厂,加载xxxAware 注册单实例bean
			prepareBeanFactory(beanFactory);

			try { 
				// 加载bean工厂的后置处理器
				postProcessBeanFactory(beanFactory);

				// 执行bean工厂的后置处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// 将bean的后置处理器注册到容器中
				registerBeanPostProcessors(beanFactory);

				// 初始化容器信息
				initMessageSource();

				// 初始化事件派发器
				initApplicationEventMulticaster();

				// 加载其他的单实例bean,并创建web容器
				onRefresh();

				// 将监听器注册到容器
				registerListeners();

				// 完成bean工厂的初始化,看看有没有自定义的类加载器和xxxAware接口,都加载到容器中
				finishBeanFactoryInitialization(beanFactory);

				//清理缓存,为此上下文初始化生命周期处理器,将容器刷新派发到生命周期处理器,事件派发器发布事件,启动web容器,并发布事件
				finishRefresh();
			}

			catch (BeansException ex) { 
				if (logger.isWarnEnabled()) { 
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally { 
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

二,自定义starter

1.原理分析

1、这个场景需要使用到的依赖是什么?

2、如何编写自动配置

@Configuration  //指定这个类是一个配置类
@ConditionalOnXXX  //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter  //指定自动配置类的顺序
@Bean  //给容器中添加组件
@ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中
自动配置类要能加载
将需要启动就加载的自动配置类,配置在META‐INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

3、模式:
启动器只用来做依赖导入;
专门来写一个自动配置模块;
启动器依赖自动配置;别人只需要引入启动器(starter)
mybatis-spring-boot-starter;自定义启动器名-spring-boot-starter

2.代码

1.首先创建两个maven工程

yhd-spring-boot-starter
yhd-spring-boot-starter-autoconfigurer

2.引入依赖

在yhd-spring-boot-starter-autoconfigurer里面引入

    <!-- 引入springboot核心启动器 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
    </dependencies>

在yhd-spring-boot-starter里面引入

        <!--  引入自动配置模块  -->
    <dependencies>
        <dependency>
            <groupId>com.yhd</groupId>
            <artifactId>yhd-spring-boot-starter-autoconfigurer</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

3.编写yhd-spring-boot-starter-autoconfigurer

1.创建一个service


public class HelloService { 

    @Autowired
    private HelloServiceProperties helloServiceProperties;


    public String sayHello(String name){ 
        return helloServiceProperties.getPrefix()+" "+name+" " +helloServiceProperties.getSuffix();
    }
}

2.创建一个HelloServiceProperties


@Component
@ConfigurationProperties(prefix = "yhd.hello")
public class HelloServiceProperties { 

    private String prefix;
    private String suffix;

    public String getPrefix() { 
        return prefix;
    }

    public void setPrefix(String prefix) { 
        this.prefix = prefix;
    }

    public String getSuffix() { 
        return suffix;
    }

    public void setSuffix(String suffix) { 
        this.suffix = suffix;
    }
}

3.创建一个HelloServiceAutoConfiguration


@SpringBootConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(HelloServiceProperties.class)
public class HelloServiceAutoConfiguration { 

    @Autowired
    private HelloServiceProperties helloServiceProperties;

    @Bean
    public HelloService helloService(){ 
       return new HelloService();
    }
}

4.在resources目录下创建META-INF目录

5.在META-INF下创建spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yhd.config.HelloServiceAutoConfiguration

4.将这两个项目安装到仓库

5.别的项目引入yhd-spring-boot-starter依赖


@RestController
public class HelloController { 

    @Autowired
    private HelloService helloService;
    @GetMapping("/hello")
    public String hello(){ 
        return helloService.sayHello("尹会东");
    }
}

配置文件:

yhd.hello.prefix=Hello
yhd.hello.suffix=!

访问localhost:8080,输出:Hello 尹会东 !。
至此,starter创建成功。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服