点击上方 "Java指南者"关注, 星标或置顶一起成长
在 SpringApplication 源码分析 中分析 run 方法的时候,可以看到调用的 prepareContext 方法如下:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
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);
}
该方法的最后通过调用 load 方法进行加载 bean 到应用上下文中:
/**
* Load beans into the application context.
* @param context the context to load beans into
* @param sources the sources to load
*/
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
//创建BeanDefinitionLoader
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
传入进 load 方法的 sources 参数是我们项目定义的启动类。
在 load 方法中会先根据上下文去获取 Bean definition registry
/**
* Get the bean definition registry.
* @param context the application context
* @return the BeanDefinitionRegistry if it can be determined
*/
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
if (context instanceof BeanDefinitionRegistry) {
return (BeanDefinitionRegistry) context;
}
if (context instanceof AbstractApplicationContext) {
return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
}
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
接着会创建 BeanDefinitionLoader 实例:
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
return new BeanDefinitionLoader(registry, sources);
}
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
// 注解形式的Bean定义读取器
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
// XML形式的Bean定义读取器
this.xmlReader = new XmlBeanDefinitionReader(registry);
// Groovy形式的Bean定义读取器
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
// 类路径扫描器
this.scanner = new ClassPathBeanDefinitionScanner(registry);
// 扫描器添加要过滤的类
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
在该构造器中,有三种形式的 Bean 定义读取器,可以通过不同的方式去读取已经定义的 Bean,另外还提供了类路径下的扫描器和过滤器。
我们来看下该类的属性除了有上面几个之外,还有一个 ResourceLoader :
另外我们来看最关键的 load 方法:
int load() {
int count = 0;
for (Object source : this.sources) {
count += load(source);
}
return count;
}
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
load 方法会统计所 load 的 source 数量,在 load 中有几种类别可以选择,比如 Class、Resource、Package、CharSequence。
而对应着这几种类别又分别有不同的 load 方法:
private int load(Class<?> source) {
if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
load(loader);
}
if (isComponent(source)) {
this.annotatedReader.register(source);
return 1;
}
return 0;
}
private int load(GroovyBeanDefinitionSource source) {
int before = this.xmlReader.getRegistry().getBeanDefinitionCount();
((GroovyBeanDefinitionReader) this.groovyReader).beans(source.getBeans());
int after = this.xmlReader.getRegistry().getBeanDefinitionCount();
return after - before;
}
private int load(Resource source) {
if (source.getFilename().endsWith(".groovy")) {
if (this.groovyReader == null) {
throw new BeanDefinitionStoreException("Cannot load Groovy beans without Groovy on classpath");
}
return this.groovyReader.loadBeanDefinitions(source);
}
return this.xmlReader.loadBeanDefinitions(source);
}
private int load(Package source) {
return this.scanner.scan(source.getName());
}
private int load(CharSequence source) {
String resolvedSource = this.xmlReader.getEnvironment().resolvePlaceholders(source.toString());
// Attempt as a Class
try {
return load(ClassUtils.forName(resolvedSource, null));
}
catch (IllegalArgumentException | ClassNotFoundException ex) {
// swallow exception and continue
}
// Attempt as resources
Resource[] resources = findResources(resolvedSource);
int loadCount = 0;
boolean atLeastOneResourceExists = false;
for (Resource resource : resources) {
if (isLoadCandidate(resource)) {
atLeastOneResourceExists = true;
loadCount += load(resource);
}
}
if (atLeastOneResourceExists) {
return loadCount;
}
// Attempt as package
Package packageResource = findPackage(resolvedSource);
if (packageResource != null) {
return load(packageResource);
}
throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
}
对于 Class 类型的在其方法内会做判断是否为 GroovyBeanDefinitionSource 和判断是否有 Component 注解。
private int load(Class<?> source) {
if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
load(loader);
}
if (isComponent(source)) {
this.annotatedReader.register(source);
return 1;
}
return 0;
}
private int load(GroovyBeanDefinitionSource source) {
int before = this.xmlReader.getRegistry().getBeanDefinitionCount();
((GroovyBeanDefinitionReader) this.groovyReader).beans(source.getBeans());
int after = this.xmlReader.getRegistry().getBeanDefinitionCount();
return after - before;
}
对于 Resource 类型的只需要根据文件结尾来判断就行了,比如是否有 .groovy 结尾。
private int load(Resource source) {
if (source.getFilename().endsWith(".groovy")) {
if (this.groovyReader == null) {
throw new BeanDefinitionStoreException("Cannot load Groovy beans without Groovy on classpath");
}
return this.groovyReader.loadBeanDefinitions(source);
}
return this.xmlReader.loadBeanDefinitions(source);
}
对于 Package 类型只需要根据给定的包名进行扫描该包就行了。
private int load(Package source) {
return this.scanner.scan(source.getName());
}
对于 CharSequence 类型的就是先将其转成字符串,然后通过反射将其转成 Class,然后调用 Class 类型的 load 方法;如果先将其用 Class load 失败的话,接着会用 Resource 的方式去 load,如果还是失败的话,最后是通过 Package 的方式去 load,如果还是不行,则会抛出异常说明这是目前不支持的 source 形式。
private int load(CharSequence source) {
String resolvedSource = this.xmlReader.getEnvironment().resolvePlaceholders(source.toString());
// Attempt as a Class
try {
return load(ClassUtils.forName(resolvedSource, null));
}
catch (IllegalArgumentException | ClassNotFoundException ex) {
// swallow exception and continue
}
// Attempt as resources
Resource[] resources = findResources(resolvedSource);
int loadCount = 0;
boolean atLeastOneResourceExists = false;
for (Resource resource : resources) {
if (isLoadCandidate(resource)) {
atLeastOneResourceExists = true;
loadCount += load(resource);
}
}
if (atLeastOneResourceExists) {
return loadCount;
}
// Attempt as package
Package packageResource = findPackage(resolvedSource);
if (packageResource != null) {
return load(packageResource);
}
throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
}
而这我们通常有哪几种方式传递进来呢?比如:
public class BootStrapMain {
public static void main(String[] args) {
// class 形式
SpringApplication.run(BootStrapMain.class, args);
// Resource 形式
//SpringApplication.run(new ClassPathResource("applicationContext.xml"), args);
// Package 形式
//SpringApplication.run(Package.getPackage("com.zhisheng.springboot.learning"), args);
// CharSequence 类型
//SpringApplication.run("classpath:/applicationContext.xml", args);
}
}
关注我
关注我,Java 学习不迷路!