本文共 3954 字,大约阅读时间需要 13 分钟。
spring是日常开发中用的非常多的一个框架,那么spring究竟是如何帮我们简化开发?短短的几行配置里,spring究竟做了啥?后续几篇博客会分析下spring的源码。
使用xml配置spring的话,这个配置可以说非常熟悉了。
然后如果想通过spring容器来加载配置这个类,简单的代码如下。
public class TestDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person = (Person) context.getBean("person"); System.out.println("person name:" + person.getName()); }}
假想下,如果让我来写spring,那么我要做的第一步是啥?我想会是找到配置文件,加载它
(这里先不管使用java配置的方式)
spring对于各种各样的资源抽象了一个接口,比如文件资源或者类路径的资源。
public interface Resource extends InputStreamSource { boolean exists(); default boolean isReadable() { return true; } default boolean isOpen() { return false; } default boolean isFile() { return false; } URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription();}
所有的资源都会通过这个类来抽象。
那么简单的说来,spring容器加载资源的第一步,就是加载配置文件,将这个配置文件转换成spring的抽象资源Resource
源码还是比较简单的
1)在构造函数里,将路径处理下(替换占位符)存储在成员变量里 2)将配置文件转换为spring的一个资源(具体步骤在loadBeanDefinition里)解析路径代码
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { //1、父类设置ResourcePatternResolver super(parent); //设置路径到configLocations成员变量里,中间会执行一步,替换${}这样的占位符, //比如路径填了 ${path}/application.xml,可以被替换为.properties里的路径 setConfigLocations(configLocations); if (refresh) { //实际启动spring容器 refresh(); }}public void setConfigLocations(String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { //把占位符给换掉 比如${path.xxx} 换成PropertyPlaceHolder的值 this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; }}
AbstractXmlApplicationContext类
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); }}
最后会回调DefaultResourceLoader的getResources方法
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); for (ProtocolResolver protocolResolver : this.protocolResolvers) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } if (location.startsWith("/")) { return getResourceByPath(location); } else if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // Try to parse the location as a URL... URL url = new URL(location); return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url)); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. // 没有填前缀,最后会被解析为 ClassPathContextResource return getResourceByPath(location); } }}protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader());}
由于这里没有配置协议前缀(比如classpath:xxx)最后资源会被解析为ClassPathContextResource
spring启动会去加载配置文件,将配置文件转换为spring可以识别的Resource
转载地址:http://ikonl.baihongyu.com/