Extended interface for a resource that is loaded from an enclosing 'context', e.g. from a javax.servlet.ServletContext but also from plain classpath paths or relative file system paths (specified without an explicit prefix, hence applying relative to the local ResourceLoader's context).
public class ProtocolResolverApplication {
public static void main(String[] args) throws Exception {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
DogProtocolResolver dogProtocolResolver = new DogProtocolResolver();
resourceLoader.addProtocolResolver(dogProtocolResolver);
}
}
然后,用 ResourceLoader 获取刚编写好的 Dog.txt ,并用缓冲流读取:
public static void main(String[] args) throws Exception {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
DogProtocolResolver dogProtocolResolver = new DogProtocolResolver();
resourceLoader.addProtocolResolver(dogProtocolResolver);
Resource resource = resourceLoader.getResource("dog:Dog.txt");
InputStream inputStream = resource.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(reader);
String readLine;
while ((readLine = br.readLine()) != null) {
System.out.println(readLine);
}
br.close();
}
运行 main 方法,控制台打印出 Dog.txt 的内容,证明 DogProtocolResolver 已经起到了作用:
wangwangwang
4.2 DefaultResourceLoader可自行加载类路径下的资源
public Resource getResource(String location) {
// ......
if (location.startsWith("/")) {
return getResourceByPath(location);
} else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
// ......
}
这部分,且不看上面的 startsWith ,只看中间的 else if 部分返回的类型,就知道它能解析类路径下的资源了,而上面的 getResourceByPath 方法,点进去发现默认还是加载类路径下:
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
@Configuration
@ComponentScan("com.linkedbear.spring.annotation.g_propertysource.bean")
@PropertySource("classpath:propertysource/jdbc.properties")
public class JdbcPropertiesConfiguration {
}
@Configuration
@ComponentScan("com.linkedbear.spring.annotation.h_propertyxml.bean")
@PropertySource("classpath:propertysource/jdbc.xml")
public class JdbcXmlConfiguration {
}
5.2.4 测试运行
编写启动类,驱动 JdbcXmlConfiguration ,并打印 IOC 容器中的 JdbcXmlProperty :
public class PropertySourceXmlApplication {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JdbcXmlConfiguration.class);
System.out.println(ctx.getBean(JdbcXmlProperty.class).toString());
}
}
好了,来解答上面那个问题:为了完成跟 .properties 文件一样的写法,反而在 xml 中要写这么一大堆乱七八糟的格式,这都哪来的???别着急,在这个问题之前,先请小伙伴思考另一个问题:SpringFramework 是怎么加载那些 .properties 文件的呢?
答案很简单嘛,肯定是走的 jdk 原生的 Properties 类咯。下面咱来解答这个问题。
5.2.5.1 解析Properties的入口
答案的追踪可以从 @PropertySource 注解的一个属性入手:
public @interface PropertySource {
// ......
/**
* Specify a custom {@link PropertySourceFactory}, if any.
* <p>By default, a default factory for standard resource files will be used.
* @since 4.3
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
public class DefaultPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}
}
默认实现中,它只是 new 了一个 ResourcePropertySource 而已,而这个构造方法中有一句让我们很敏感的方法调用:PropertiesLoaderUtils.loadProperties
/**
* Create a PropertySource having the given name based on Properties
* loaded from the given encoded resource.
*/
public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
super(name, PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = getNameForResource(resource.getResource());
}
进入这个 loadProperties 方法中:
public static Properties loadProperties(EncodedResource resource) throws IOException {
Properties props = new Properties();
fillProperties(props, resource);
return props;
}
得了,它的底层果然是这么用的,那问题自然也就解开了。@PropertySource 解析 xml 也是用 Properties 这个类解析的。可是我们在之前的 JavaSE 中可能没学过 Properties 解析 xml 啊,这还第一次听说咧。
当然,properties 也不是完全 OK ,由于它的特征是 key-value 的形式,整个文件排下来是没有任何层次性可言的(换句话说,每个配置项之间的地位都是平等的)。这个时候 xml 的优势就体现出来了,它可以非常容易的体现出层次性,不过咱不能因为这一个点就觉得 xml 还可以,因为有一个更适合解决这个问题的配置格式:yml 。
@Configuration
@ComponentScan("com.linkedbear.spring.annotation.i_propertyyml.bean")
@PropertySource("classpath:propertysource/jdbc.yml")
public class JdbcYmlConfiguration {
}
public class PropertySourceYmlApplication {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JdbcYmlConfiguration.class);
System.out.println(ctx.getBean(JdbcYmlProperty.class).toString());
}
}