springboot 自动化装配机制(二)

从启动类入手

通常来讲,一个spring boot项目每每会有一个启动类,web

/**
 * @Author: 王博  
 * @Date: 2019/5/8 17:19 2019
 */

@SpringBootApplication
@RestController
public class TestDemo {
    @Autowired
    private JedisClusterTemplate jedisClusterTemplate;



    @Autowired
    private ShardedJedisTemplate template;
    @RequestMapping("/")
    public String hello() {
        template.set("wangbo","hahahah");
        return "hello";

    }


    public static void main(String[] args) {
        SpringApplication.run(TestDemo.class, args);
    }
}

复制代码

在这个启动类里面SpringApplication.run(TestDemo.class, args); 的这行代码是故事的开始。 短短的一行代码,背后发生了不少细节,当这个方法执行完,相关的starter就被执行了因此咱们的JedisClusterTemplate 就被注入在容器中了。 打开浏览器访问这个hello方法,debug到template.set("wangbo","hahahah");上,发现redis是能够连通的。redis

这就是神奇的地方,带着疑问去剖析这行短短的代码。咱们一层层进入这个代码你会来到如下这个方法里面。spring

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
		return new SpringApplication(sources).run(args);
	}

复制代码

能够看到首先建立了一个SpringApplication对象,构造函数的参数则是咱们的启动类TestDemo再进入这个构造函数设计模式

public SpringApplication(Object... sources) {
		initialize(sources);
	}
	
@SuppressWarnings({ "unchecked", "rawtypes" })
	private void initialize(Object[] sources) {
		if (sources != null && sources.length > 0) {
		
	        // 启动类被加入到sources里面了.
			this.sources.addAll(Arrays.asList(sources));
		}
		this.webEnvironment = deduceWebEnvironment();
		
		//设置一些initializer
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		
		//设置一些listeners 这个很重要,核心内容。
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}	
	
复制代码

再来看看很是核心的一个方法getSpringFactoriesInstances() 这个方法上面的代码用到了2次,主要是用来实例化spring.factories里面配置的类,这个方法特别核心以下:浏览器

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<String>(
		            //从META-INF/spring.factories 路径下获取名称
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		
		//根据这些类的名称来实例化这些类的对象。
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
	
复制代码
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		try {
			Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			List<String> result = new ArrayList<String>();
			while (urls.hasMoreElements()) {
				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 ex) {
			throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
					"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}
复制代码

FACTORIES_RESOURCE_LOCATION 就是"META-INF/spring.factories"。springboot

回到initialize方法 能够看到一些listener被实例化了,而这些实例化对象的接口是ApplicationListener.class,咱们全局搜一搜这个接口对应的实现类,能够从一个spring.factories文件中看到以下配置bash

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

复制代码

能够看到一共有这么多listeners,这些listeners的做用范围是整个spring boot 启动生命周期,在这个生命周期的各个环节,spring boot都会发送一些事件给这些listener,事件由这些listener处理,完成对应的逻辑(例如在environment 准备好以后,会发送一个事件给ConfigFileApplicationListener,而后这个类将项目配置文件yml或properties的内容以PropertySource的形式加入到environment中)。 这涉及到spring 的事件机制,而spring的事件机制涉及到设计模式中的观察者模式。app

能够观察以上类图,事件将被SimpleApplicationEventMulticaster发送到注册到上下文中的listeners中,其中一个叫作ConfigFileApplicationListener的监听者会把项目配置以PropertySource的形式加入到环境中 。函数

具体实现请看接下来的文章 springboot 自动化装配机制(二)ui

相关文章
相关标签/搜索