本文共 14138 字,大约阅读时间需要 47 分钟。
工作中对于我们java开发者来说最经常使用的框架就是spring了,那么了解spring的基础原理对于我们的能力提升具有很大的好处。首先,作为框架首先它肯定还是从java基础演变而来,也就是说框架的代码都是基于我们日常使用的继承、多态已经各种设计模式的整合而抽取出来的一套规范。我们开发项目要想搭乘spring的快车就需要按规范做事,按规范开发。那么规范的熟悉程度就等价于项目质量的高低。当然框架的基础都是好的idea,好的idea不仅兼容一切还简单。这就和物理界的大一统理论一样。怎么做不重要,重要的是怎么兼容一切,因为做的办法会有无限种。实在无法实现也可以同缩小解的范围。废话不说了。让我们开启spring的流浪之旅。
工作中因为使用的是springboot,因此我们就直接从springboot来分析spring。
public static void main(String[] args) {SpringApplication.run(SimpleAdminService.class, args)}
我们看到是springAppcliatIion这个类启动的,我们跟踪一下
public static ConfigurableApplicationContext run(Class primarySource, String... args) { return run(new Class[]{primarySource}, args);}public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args);}
发现在第二个run方法的时候进行调用了springApplication的初始化方法。那么在初始化方法中又做了哪些事情?
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.sources = new LinkedHashSet();//打印sprnigboot的启动标志的模式 this.bannerMode = Mode.CONSOLE;//日志的标志 this.logStartupInfo = true;//启动命令的标志 this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.isCustomEnvironment = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); //用来判断是那种应用,对后边创建application的类型有作用this.webApplicationType = WebApplicationType.deduceFromClasspath();//加载spring.factories中注册的bean this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));//读取spring.factories文件加载applicationListener类然后实例化并返回 this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass();}发现在合理做了一些配置,主要是针对springApplication这个类的。那么是如何读这个配置文件的?跟踪代码,这块的代码主要是将spring.factories中注册的bean全部放到缓存中。private CollectiongetSpringFactoriesInstances(Classtype) { return this.getSpringFactoriesInstances(type, new Class[0]);}private SpringApplicationRunListeners getRunListeners(String[] args) { Class[] types = new Class[]{SpringApplication.class, String[].class}; return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}private CollectiongetSpringFactoriesInstances(Classtype) { return this.getSpringFactoriesInstances(type, new Class[0]);}private CollectiongetSpringFactoriesInstances(Classtype, Class[] parameterTypes, Object... args) { ClassLoader classLoader = this.getClassLoader(); Setnames =new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); Listinstances =this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances;}private ListcreateSpringFactoriesInstances(Classtype, Class[] parameterTypes, ClassLoader classLoader, Object[] args, Setnames) { Listinstances =new ArrayList(names.size()); Iterator var7 = names.iterator(); while(var7.hasNext()) { String name = (String)var7.next(); try { Class instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable var12) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12); } } return instances;}private static Map< span="">, List> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap < span="">, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { Enumerationurls = classLoader !=null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } }
继续跟踪代码。进入了springApplication类的核心区。
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start();//创建一个上下文,这个是接口。 ConfigurableApplicationContext context = null; CollectionexceptionReporters =new ArrayList(); this.configureHeadlessProperty();//拿到spring.factories文件,找到获取springApplication的监听类。 SpringApplicationRunListeners listeners = this.getRunListeners(args);//启动spring.factories中的bean listeners.starting(); Collection exceptionReporters; try {//将传入的参数进行封装 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//将传入的参数封装为configurableEnvironment ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment);//打印启动的环境spring.profiles.active Banner printedBanner = this.printBanner(environment);//创建ioc容器 context = this.createApplicationContext();//处理异常的,也是从spring.factories中读取的。 exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);//ioc的前置处理 this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);//核心方法 this.refreshContext(context);//ioc的后置处理,但是为空 this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } //对监听的器发布已经启动的消息 listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try {//向监听器广播项目已经启动的消息 listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); }}
那么getRunListeners是做什么的?
private SpringApplicationRunListeners getRunListeners(String[] args) { Class[] types = new Class[]{SpringApplication.class, String[].class}; return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}
我们能发现和之前获取spring.factories的过程一样,但获取的是ApplicationListen的类。那么我们看一下listeners.starting();
public void starting() { Iterator var1 = this.listeners.iterator(); while(var1.hasNext()) { SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next(); listener.starting(); }}
发现通过轮询所有的listener,然后逐个去调用onApplicationEvent方法。那么如何自定义和一个springBootApplicationListen?
@Data@AllArgsConstructorpublic class TestApplicationListener implements SpringApplicationRunListener { public TestApplicationListener(SpringApplication application, String[] args){ System.out.println("constructor"); } @Override public void starting() { System.out.println("start"); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { System.out.println(environment.toString()); } @Override public void contextPrepared(ConfigurableApplicationContext context) { System.out.println("拿到容器"); } @Override public void contextLoaded(ConfigurableApplicationContext context) { System.out.println("容器加载完毕"); } @Override public void started(ConfigurableApplicationContext context) { } @Override public void running(ConfigurableApplicationContext context) { System.out.println("run方法"); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { System.out.println("失败"); }}
在META-INF的spring.factories中加入该监听器的地址
org.springframework.boot.SpringApplicationRunListener=\com.scaffold.simple.admin.TestApplicationListener
启动的效果
通过例子,我们知道spring是轮询的找到注册到spring.factories中的applicationrunlisenner,然后调用starting方法。
我们看一下prepareEnvironment方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = this.getOrCreateEnvironment(); this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());//广播配置准备结束的消息 listeners.environmentPrepared((ConfigurableEnvironment)environment);//将配置绑定到springapplication this.bindToSpringApplication((ConfigurableEnvironment)environment); if (!this.isCustomEnvironment) { environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass()); } ConfigurationPropertySources.attach((Environment)environment); return (ConfigurableEnvironment)environment;}
在createApplicationContext创建ioc容器的时候,因为的springApplication构造方法的时候已经决定了webapplicationType,因此这块就直接创建了。咱们一般web都是走的AnnotationConfigReactiveWebServerApplicationContext的容器。
protected ConfigurableApplicationContext createApplicationContext() { Class contextClass = this.applicationContextClass; if (contextClass == null) { try { switch(this.webApplicationType) { case SERVLET: contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"); break; case REACTIVE: contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"); break; default: contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext"); } } catch (ClassNotFoundException var3) { throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3); } } return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);}
在前置处理的是怎么处理的呐?
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {//ioc容器中设置envirment context.setEnvironment(environment);//给ioc容器设置一些辅助类 this.postProcessApplicationContext(context);//将spring.factories中读取的实现了ApplicationContextInitializer并初始化到容器中 this.applyInitializers(context);//对监听器进行广播 listeners.contextPrepared(context); if (this.logStartupInfo) { this.logStartupInfo(context.getParent() == null);//打印spring.profiles.active this.logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); }//这里的source就是启动类。 Setsources =this.getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); this.load(context, sources.toArray(new Object[0]));//容器广播 listeners.contextLoaded(context);}
通过跟踪this.load方法
protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); }//创建beanDefinitionLoader BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) {//设置bean名称的生成器 loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load();}
加载的最后大概是将启动类注册到beandefinition中了。
private int load(Class source) { if (this.isGroovyPresent() && BeanDefinitionLoader.GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { BeanDefinitionLoader.GroovyBeanDefinitionSource loader = (BeanDefinitionLoader.GroovyBeanDefinitionSource)BeanUtils.instantiateClass(source, BeanDefinitionLoader.GroovyBeanDefinitionSource.class); this.load(loader); } if (this.isComponent(source)) { this.annotatedReader.register(new Class[]{source}); return 1; } else { return 0; }}
通过一大篇的load,然后通过静态方法将该bean注册到了容器中了。
声明:以上代码解析为个人观点,不对之处,希望指正。谢谢!
转载地址:http://tqkmi.baihongyu.com/