一.理论
在spring中经常有读取配置文件的需求,这里就会用到一个Spring提供的Resource接口
Resource 接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。Resource 接口主要提供了如下几个方法:
- getInputStream():定位并打开资源,返回资源对应的输入流。每次调用都返回新的输入流。调用者必须负责关闭输入流。
- exists():返回 Resource 所指向的资源是否存在。
- isOpen():返回资源文件是否打开,如果资源文件不能多次读取,每次读取结束应该显式关闭,以防止资源泄漏。
- getDescription():返回资源的描述信息,通常用于资源处理出错时输出该信息,通常是全限定文件名或实际 URL。
- getFile:返回资源对应的 File 对象。
- getURL:返回资源对应的 URL 对象。
Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑。
Resource 和策略模式 Resource 接口就是策略模式的典型应用,Resource 接口就代表资源访问策略,但具体采用哪种策略实现,Resource 接口并不理会。客户端程序只和 Resource 接口耦合,并不知道底层采用何种资源访问策略,这样应用可以在不同的资源访问策略之间自由切换。
Spring 为 Resource 接口提供了如下实现类:
- UrlResource:访问网络资源的实现类。
- ClassPathResource:访问类加载路径里资源的实现类。
- FileSystemResource:访问文件系统里资源的实现类。
- ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
- InputStreamResource:访问输入流资源的实现类。
- ByteArrayResource:访问字节数组资源的实现类。
这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。
二.源码
这里跟踪ClassPathXmlApplicationContext对象的创建,来了解Spring底层怎么使用Resource接口及其实现类达成策略模式
策略模式的优势 当 Spring 应用需要进行资源访问时,实际上并不需要直接使用 Resource 实现类,而是调用 ApplicationContext 实例的 getResource() 方法来获得资源,ApplicationContext 将会负责选择 Resource 的实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来,这就体现了策略模式的优势。
1 //1.根据传入的配置文件地址,创建容器对象 ClassPathXmlApplicationContext 2 public ClassPathXmlApplicationContext(String configLocation) throws BeansException { 3 this(new String[] {configLocation}, true, null); 4 }
1 //2.1中代码调用到此处,此处调用父类构造函数 ClassPathXmlApplicationContext 2 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 3 throws BeansException { 4 5 super(parent); 6 setConfigLocations(configLocations); 7 if (refresh) { 8 refresh(); 9 } 10 }
1 //3.2中代码一步步调用到此处 AbstractApplicationContext 2 public AbstractApplicationContext(ApplicationContext parent) { 3 this(); 4 setParent(parent); 5 }
1 //4.3中代码调用此处 AbstractApplicationContext 2 public AbstractApplicationContext() { 3 this.resourcePatternResolver = getResourcePatternResolver(); 4 }
1 //5.返回能将1中传入的configLocation转换成Resource对象的ResourcePatternResolver AbstractApplicationContext 2 protected ResourcePatternResolver getResourcePatternResolver() { 3 return new PathMatchingResourcePatternResolver(this); 4 }
1 //6.真正用到策略模式的地方,根据location的内容,决定返回对应的Resource实现,这个过程对客户来说是透明的 DefaultResourceLoader 2 @Override 3 public Resource getResource(String location) { 4 Assert.notNull(location, "Location must not be null"); 5 6 for (ProtocolResolver protocolResolver : this.protocolResolvers) { 7 Resource resource = protocolResolver.resolve(location, this); 8 if (resource != null) { 9 return resource; 10 } 11 } 12 13 if (location.startsWith("/")) { 14 return getResourceByPath(location); 15 } 16 else if (location.startsWith(CLASSPATH_URL_PREFIX)) { 17 return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); 18 } 19 else { 20 try { 21 // Try to parse the location as a URL... 22 URL url = new URL(location); 23 return new UrlResource(url); 24 } 25 catch (MalformedURLException ex) { 26 // No URL -> resolve as resource path. 27 return getResourceByPath(location); 28 } 29 } 30 }
参考:https://www.ibm.com/developerworks/cn/java/j-lo-spring-resource/