入口代码:
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
1.配置文件封装
在java中,可以将不同来源的资源文件抽象成URL;
**补:URL和URI的区别:
1.URI包含URL,URL定义了资源路径
2.URL不仅定义了资源路径,还定义了资源的获取方式,比如:**
http://file.zip
ftp://file.zip
所以可以通过注册不同的handler来实现不同的文件读取逻辑,比如 http:、ftp等等
但是URL并没有定义类似classpath或者ServletContext等URL的handler,虽然可以通过注册自己的URLStreamHandler来读取,但是还需要了解Java中URL的底层实现机制,而且URL接口没有定义我们需要的一些方法,比如文件是否存在、。当前文件是否可读等等,所以还是自己定义一组读取URL的抽象组件,也就是下面的Resource底层资源封装接口。
public interface InputStreamSource {
...
}
public interface Resource extends InputStreamSource {
...
}
InputStreamSource只封装了一个方法:
InputStream getInputStream();
这个方法封装任何返回InputStream的类,比如File、ClassPath等等。
Resource接口封装了很多关于文件的逻辑方法:
/**
文件状态方法
*/
boolean exists();
boolean isReadable();
boolean isOpen();
/**
文件类型转换
*/
URL getURL();
File getFile();
URI getURI();
/**
文件信息
*/
long lastModified();
String getFileName();
String getDescription();
//创建相对资源
Resource createRelative();
对于不同的文件,Resource接口有着不同的实现类,比如ClassPathResource,FileResource,UrlResource等等。
Resource resource = new ClassPathResource("applicationContext");
InputStream is = resource.getInputStream();
对于Resource是怎么实现的,如果是ClassPathResource,是利用ClassLoader提供的底层方法实现的,FileResource是根据File类直接创建FileInputStream类。
//ClassPathResource
if(this.clazz != null){
inputStream = this.clazz.getResourceAsStream(this.path);
}
ClassLoader加载类路径资源详解:
ClassLoader详解
将配置文件封装好后,其读取工作就交给XmlBeanDefinitionReader来处理了。
public XmlBeanFactory(Resource resource){
this(resource,null);
}
public XmlBeanFactory(Resource resource,BeanFactory parentBeanFactory){
super(parentBeanFactory);
//真正加载数据的地方
this.reader.loadBeanDefinitions(resource);
}
在真正加载配置文件之前,会调用父类的构造器,其代码片段如下:
public AbstractAutowireCapableBeanFactory(){
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.calss);
}
这里对ignoreDependencyInterface方法进行一下特别说明:其功能主要是忽略给定的接口的自动装配功能。
ignoreDependencyInterface和ignoreDependencyType
这两个方法都是在spring装配bean时需要忽略装配某些属性时使用的,但是还是有区别;
ignoreDependencyType
比如ignoreDependencyInterface(A.class);
class B{
private A a;+set、get方法
}
注意:这里使用bean标签进行依赖注入,用autowire注解不适用此方式。
如果在初始化B的时候还未初始化A,会忽略A属性并置为null;
ignoreDependencyInterface
这个方法比较复杂,并不是指实现该接口的类不会被自动装配到某个类的属性当中,而这个方法的真正意思是忽略该接口的实现类中和接口中setter方法入参类型相同的依赖。举个例子:
public interface A{
void setB(B b);
void setC(C c)
}
//使用bean标签装备AImpl
public class AImpl implements A{
private B b;
private C c;
public void setB(B b){
this.b = b;
}
public void setC(C c){
this.c = c;
}
}
这样ignoreDependencyInterface(A.class)后,就会忽略掉AImpl中的b、c属性装配。
2.加载Bean
入口方法:
//reader是XmlBeanDefinitionReader
this.reader.loadBeanDefinition(resource);
第一步:对Resource对象进行进一步封装;
使用EncodedResource类对Resource进行进一步封装。
EncodedResource主要作用是Resource进行编码的,其中最主要的方法时:
public Reader getReader(){
if(this.encoding != null){
return new InputStreamReader(this.resource.getInputStream(),this.encoding);
}
}
说白了这个类就是用于返回带编码的Reader实现类InputStreamReader。
然后转到了可以复用的loadBeanDefinitions(new EncodedResource())方法
这次才是真正的数据准备阶段
第二步:获取输入流并构造InputSource;
InputSource是SAX解析中的类,用于解析xml文档
InputStream inputStream = encodedResource.getReader().getInputStream();
InputSource inputSource = new InputSource(inputStream);
//设置inputSource的编码
inputSource.setEncoding(encodedResource.getEncoding());
第三步:继续调用doLoadBeanDefinitions;
doLoadBeanDefinitions(inputSource,resource);
这里只做了三件事:
1.获取xml验证模式
2.加载xml,获取Document
3.通过Document注册Bean