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

相關文章
相關標籤/搜索