Spring源码深度解析-第二章

  入门级别的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容器的基本功能简要:

  1. 读取配置文件
  2. 解析出配置文件中的类,并进行实例化
  3. 获取实例化的对象,提供使用

  对于以上第一步,读取配置文件,简单代码如下:

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行

猜你喜欢

转载自www.cnblogs.com/hellohello/p/9427960.html