入门级别的spring配置文件
<beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <beanid="springHelloWorld" class="com.tutorial.spring.helloworld.impl.SpringHelloWorld"></bean> <beanid="strutsHelloWorld" class="com.tutorial.spring.helloworld.impl.StrutsHelloWorld"></bean> <beanid="helloWorldService" class="com.tutorial.spring.helloworld.HelloWorldService"> <propertyname="helloWorld"ref="springHelloWorld"/> </bean> </beans>
Spring容器的基本功能简要:
- 读取配置文件
- 解析出配置文件中的类,并进行实例化
- 获取实例化的对象,提供使用
对于以上第一步,读取配置文件,简单代码如下:
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("config.xml"));
在Java中,不同来源的资源被抽象成URL,这些资源具有不同的协议,如:"file:","http:","jar:"等,却没有"classpath:",所以Spring对要使用的资源文件进行了封装,相关接口如下:
public interface InputStreamSource { InputStream getInputStream() throws IOException; } public interface Resource extends InputStreamSource { boolean exists(); default boolean isReadable() { return exists(); } default boolean isOpen() { return false; } default boolean isFile() { return false; } URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); } long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; @Nullable String getFilename(); String getDescription(); }
以上ClassPathResource是Resource接口的实现,除此之外,还有其他的实现,如:FileSystemResource、UrlResource、InputStreamResource、ByteArrayResource等。通过这些Resource的实现类,在Spring中可以快速地定位和使用资源(getInputSteam就可以获取到对应资源的流)。
通过Resource的实现类获取到了Spring配置文件之后,传递给了XmlBeanFactory的构造函数:
1 public class XmlBeanFactory extends DefaultListableBeanFactory { 2 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); 3 4 public XmlBeanFactory(Resource resource) throws BeansException { 5 this(resource, null); 6 } 7 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { 8 super(parentBeanFactory); 9 this.reader.loadBeanDefinitions(resource); 10 } 11 }
从18行一直点击去,代码如下:
public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); } public void ignoreDependencyInterface(Class<?> ifc) { this.ignoredDependencyInterfaces.add(ifc); }
可以发现,有三个类被添加到了ignoredDependencyInterfaces集合中,这个集合中的类不会被自动装配,如代码:
@Autowired
BeanNameAware obj;
这个obj就不会被装配。被添加的这三个类都是接口,用于bean感知spring的存在,java对象被spring容器创建并且管理期间,java对象无法感知这个过程,而如果这个对象类实现了以上三个接口,则可以获取到spring框架的相关信息,分别是:作为bean时的名称、bean容器的引用和加载这个bean的classLoader。
回到XmlBeanFactory的源码,对资源起加载作用的是第9行:
this.reader.loadBeanDefinitions(resource);
点进去,代码如下:
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
通过EncodedResource对资源进行再次包装,这里简单介绍一下这个EncodedResource类:
public class EncodedResource implements InputStreamSource { public EncodedResource(Resource resource) { this(resource, null, null); } private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) { super(); Assert.notNull(resource, "Resource must not be null"); this.resource = resource; this.encoding = encoding; this.charset = charset; } public Reader getReader() throws IOException { if (this.charset != null) { return new InputStreamReader(this.resource.getInputStream(), this.charset); } else if (this.encoding != null) { return new InputStreamReader(this.resource.getInputStream(), this.encoding); } else { return new InputStreamReader(this.resource.getInputStream()); } } // ... 省略代码 }
这个类包装了资源的字符编码相关的操作。编码后的资源被这样使用:
InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); }
可见,资源被传递到了doLoadBeanDefinitions的另一个重载版本中(对异常的处理细度从高到低):
1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 2 throws BeanDefinitionStoreException { 3 4 try { 5 Document doc = doLoadDocument(inputSource, resource); 6 int count = registerBeanDefinitions(doc, resource); 7 if (logger.isDebugEnabled()) { 8 logger.debug("Loaded " + count + " bean definitions from " + resource); 9 } 10 return count; 11 } 12 catch (BeanDefinitionStoreException ex) { 13 throw ex; 14 } 15 catch (SAXParseException ex) { 16 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 17 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); 18 } 19 catch (SAXException ex) { 20 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 21 "XML document from " + resource + " is invalid", ex); 22 } 23 catch (ParserConfigurationException ex) { 24 throw new BeanDefinitionStoreException(resource.getDescription(), 25 "Parser configuration exception parsing XML from " + resource, ex); 26 } 27 catch (IOException ex) { 28 throw new BeanDefinitionStoreException(resource.getDescription(), 29 "IOException parsing XML document from " + resource, ex); 30 } 31 catch (Throwable ex) { 32 throw new BeanDefinitionStoreException(resource.getDescription(), 33 "Unexpected exception parsing XML document from " + resource, ex); 34 } 35 }
#XmlBeanDefinitionReader 388行