通常来讲,一个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