前言
Spring的出现,把Java net包下的URL的资源类型做了扩展。明确了资源的定位及表示的边界。解耦了资源的定位和表示,以下将整理Spring为统一资源管理做出的努力。(文末给出获取bean的工具类,得益于Spring对资源的重新设计。)
Java 对URL的狭义解释
广义的URL解释 ,大意为:
因特网上访问某一资源的唯一标志:形如<方案>:<方案描述部分>
,以下都为URL的表示
- http://www.baidu.com
- file://SD/packet.xml
- thunder://QUFodHRwOi8vM
如果将以上的URL告诉浏览器,浏览器会使用对应的方案(软件),委托方案(软件)执行方案描述部分。如使用file方案定位到/SD//packet.xml ,即打开本地的资源管理器到目录//SD/packet.xml下。
Java8
中.net
下的URL是提供了getFile()
方法,但是形同虚设
Spring 重新设计了对资源的抽象
org.springframework.core.io.Resource
作为资源的抽象和访问接口。URL使用file方案的标准
:
- 还有其他URL支持的资源不一一展示,但是Spring 对资源的解读明显是比原生Java更具体的
Spring 获取更广义的URL —— ResourceLoader
形如<方案>:<方案描述部分>
-
DefaultResourceLoader
从classpath中获取,明确了<方案>中是file与http的返回URLResource,未明确返回ClasspathResource -
FileSystemResourceLoader
明确了<方案>中是file与http的返回URLResource,未明确返回FileSystemResource -
PathMatchingResourcePatternResolver
,默认使用DefaultResourceLoader
,并支持批量的资源访问,也可以通过构造方法传入FileSystemResourceLoader
。是资源访问的组合拳(功能完备)
为什么要交代ResourceLoader ?
- 书上没有说,从结果上来看是更好得支持了本地文件的访问,同时解耦了资源的表现,在未来支持
Docker
资源的访问变成了可能。 Spring
使用PathMatchingResourcePatternResolver
支持统一资源的加载,一般用到classpath
下的文件即可,传统的URL
习惯告诉程序单个文件的位置,Spring
则在URL规范下引入了一种新的协议前缀classpath
,Eg:classpath:conf/container-conf.xml
, 这种规范为外部化配置打下了基础,也更好得区分本地磁盘中的其他资源
ApplicationContext 作为资源管理者的角色
- 通常,所有的
ApplicationContex
t实现类会直接会间接地继承AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
/** ResourcePatternResolver used by this context. */
private ResourcePatternResolver resourcePatternResolver;
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
}
以上源码告诉我们的事实:
-
ApplicationContex实现类
自己可以作为DefaultResourceLoader
—— 可访问classpath资源或URL资源 -
ApplicationContex实现类
持有带有this
的PathMatchingResourcePatternResolver
值得一提的是这个this是设计模式中:模板方法的一种使用场景,此时的this代表实现类,默认的实现类一定是DefaultResourceLoader
(由AbstractApplicationContext extends DefaultResourceLoader
决定)。 -
对于获取本地的文件,有实现类
FileSystemXmlApplicationContext
,其中改写了方法
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
// 原来的方法
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
解耦了资源表示,拓展就变得方便多了
ApplicationContext 与接口注入
- 随意找了一个网上的
Spring 上下文工具类 (用于在运行时获取Bean)
,来看看它怎么利用ApplicationContext
的
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
- 接口注入的规范
Spring 提供了三种依赖注入的实现,其中入侵性比较强的是接口注入,唯一的好处是不需要写xml文件,这里的工具类就是使用这种注入方式。(这个是依赖容器启动时自动扫描接口的特性属于IOC的内容)
public interface ApplicationContextAware extends Aware {
/**
* 容器启动后,会把applicationContext实现类注入到工具类里
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}