美文网首页
BeanFactory和ApplicationContext

BeanFactory和ApplicationContext

作者: 言西枣 | 来源:发表于2017-08-02 15:06 被阅读44次

    概述

    BeanFatory作为Spring的IOC容器主要提供了注册Bean和管理依赖这两个功能,它的接口包括如下函数:

    BeanFactory.png
    ApplicationContext在间接继承了BeanFactory的基础上,还多继承了四个接口,提供了更多的功能,如事件发布,资源载入,消息等。
    BeanFactory and ApplicationContext

    实现

    BeanFactory接口的实现为了实现注册Bean的功能,需要使用BeanDefinitionRegistry这个接口,这三者的关系如图:

    BeanFactoryAndBeanDefinitionRegistry.png
    可以看到,BeanFactory的实现同时实现了这两个接口,而它的内部,正是使用BeanDefinition来管理Bean: DefaultListableBeanFactory.png
    无论是通过代码还是配置文件或者注解,本质上都是通过BeanDefinitionReader先构造BeanDefinition,然后将BeanDefinition注册到BeanDefinitionRegistry,通常也就是BeanFactory的实现。

    IoC容器的两个阶段

    容器启动阶段
    • 加载配置
    • 分析配置信息
    • 装配到BeanDefinition
    • PostProcess

    BeanFactoryPostProcessor是容器的扩展机制,可在实例化阶段之前对BeanDefinition中的信息进行修改
    如PropertyPlaceHolderConfigurer,把它声明为Bean,ApplicationContext会注册并应用它,将bean配置中的占位符用属性文件中的值替换

    Bean实例化阶段
    • 实例化对象
    • 装配依赖
    • 生命周期回调
    • 对象的其他处理
    • 注册回调接口
    Bean生命周期
    1. 实例化
      BeanFactory根据BeanDefinition来进行实例化,使用策略模式有反射和cglib两种方式来实例化bean,返回一个包裹了bean的BeanWrapper,用于第二步设置属性
    2. 设置属性
      BeanWrapperImpl同时也是继承了PropertyEditorRegistry,使用PropertyEditor来转换类型,设置属性
    3. Aware接口
      BeanFactory: BeanNameAware BeanClassLoaderAware BeanFactoryAware
      ApplicationContext: ResourceLoaderAware ApplicationEventPublisherAware MessageSourceAware ApplicationContextAware(都是ApplicationContext)
    4. BeanPostProcessor
    public interface BeanPostProcessor {
    
        /**
         * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
         * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
         * or a custom init-method). The bean will already be populated with property values.
         * The returned bean instance may be a wrapper around the original.
         * @param bean the new bean instance
         * @param beanName the name of the bean
         * @return the bean instance to use, either the original or a wrapped one; if
         * {@code null}, no subsequent BeanPostProcessors will be invoked
         * @throws org.springframework.beans.BeansException in case of errors
         * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
         */
        Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    
        /**
         * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
         * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
         * or a custom init-method). The bean will already be populated with property values.
         * The returned bean instance may be a wrapper around the original.
         * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
         * instance and the objects created by the FactoryBean (as of Spring 2.0). The
         * post-processor can decide whether to apply to either the FactoryBean or created
         * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
         * <p>This callback will also be invoked after a short-circuiting triggered by a
         * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
         * in contrast to all other BeanPostProcessor callbacks.
         * @param bean the new bean instance
         * @param beanName the name of the bean
         * @return the bean instance to use, either the original or a wrapped one; if
         * {@code null}, no subsequent BeanPostProcessors will be invoked
         * @throws org.springframework.beans.BeansException in case of errors
         * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
         * @see org.springframework.beans.factory.FactoryBean
         */
        Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    
    }
    

    常用场景:1.处理Aware接口 2.生成当前对象的代理(AOP)
    ApplicationContextAwareProcessor处理ApplicationContext相关的各个Aware接口。

    用BeanPostProcessor处理Marker接口时(如Aware接口),其实这个接口相当于为这个Bean添加了新的属性,接口一般包括的函数只有设置属性和获取属性,获取属性由Bean的Client调用,设置属性的函数则由BeanPostProcessor调用来进行设置,所以这种Marker接口一般是在BeanPostProcessor的Before函数中处理,紧邻着Bean实例化过程中的设置属性(因为这也是设置属性的一种,只不过是所有Bean统一设置)。

    相对的,使用BeanPostProcessor来生成proxy时会在after函数中处理,这时候bean的初始化已经完成,可以被代理了。

    1. InitializingBean和init-method
      Bean的生命周期接口和生命周期方法

    2. 注册销毁回调
      以上的设置都完成之后,如果Bean实现了DisposableBean接口或者声明了destroy-method属性,那么这时候容器会为这个Bean注册一个用于销毁的回调,当关闭容器的时候,主动调用一个函数,来执行bean的销毁工作。

    ConfigurableBeanFactory
    /**
         * Destroy all singleton beans in this factory, including inner beans that have
         * been registered as disposable. To be called on shutdown of a factory.
         * <p>Any exception that arises during destruction should be caught
         * and logged instead of propagated to the caller of this method.
         */
        void destroySingletons();
    
    ConfigurableApplicationContext
        /**
         * Register a shutdown hook with the JVM runtime, closing this context
         * on JVM shutdown unless it has already been closed at that time.
         * <p>This method can be called multiple times. Only one shutdown hook
         * (at max) will be registered for each context instance.
         * @see java.lang.Runtime#addShutdownHook
         * @see #close()
         */
        void registerShutdownHook();
    
    1. 使用

    ApplicationContext

    • BeanFactoryPostProcessor,BeabPostProcessor等特殊bean的自动识别
    • bean实例的自动初始化
    • 统一的资源加载策略
    • 国际化的信息支持
    • 容器内事件发布
    资源加载

    问题:

    1. java.net.URL局限于http,ftp,file等协议,而资源可以以二进制、字节流、文件等形式存在于文件系统、classpath、URL定位等地方
    2. java.net.URL对资源的查找和定位没有清晰的界限,返回的形式多种多样,没有统一的抽象

    解决:
    Resource与ResourceLoader

    ClassPathResource class|classLoader#getResourceAsStream
    FileSystemResource
    UrlResource   UrlConnection#getInputStream
    ByteArrayResource
    InputStreamResource 
    
    public interface Resource extends InputStreamSource {
    
        /**
         * Return whether this resource actually exists in physical form.
         * <p>This method performs a definitive existence check, whereas the
         * existence of a {@code Resource} handle only guarantees a
         * valid descriptor handle.
         */
        boolean exists();
    
        /**
         * Return whether the contents of this resource can be read,
         * e.g. via {@link #getInputStream()} or {@link #getFile()}.
         * <p>Will be {@code true} for typical resource descriptors;
         * note that actual content reading may still fail when attempted.
         * However, a value of {@code false} is a definitive indication
         * that the resource content cannot be read.
         * @see #getInputStream()
         */
        boolean isReadable();
    
        /**
         * Return whether this resource represents a handle with an open
         * stream. If true, the InputStream cannot be read multiple times,
         * and must be read and closed to avoid resource leaks.
         * <p>Will be {@code false} for typical resource descriptors.
         */
        boolean isOpen();
    
        /**
         * Return a URL handle for this resource.
         * @throws IOException if the resource cannot be resolved as URL,
         * i.e. if the resource is not available as descriptor
         */
        URL getURL() throws IOException;
    
        /**
         * Return a URI handle for this resource.
         * @throws IOException if the resource cannot be resolved as URI,
         * i.e. if the resource is not available as descriptor
         */
        URI getURI() throws IOException;
    
        /**
         * Return a File handle for this resource.
         * @throws IOException if the resource cannot be resolved as absolute
         * file path, i.e. if the resource is not available in a file system
         */
        File getFile() throws IOException;
    
        /**
         * Determine the content length for this resource.
         * @throws IOException if the resource cannot be resolved
         * (in the file system or as some other known physical resource type)
         */
        long contentLength() throws IOException;
    
        /**
         * Determine the last-modified timestamp for this resource.
         * @throws IOException if the resource cannot be resolved
         * (in the file system or as some other known physical resource type)
         */
        long lastModified() throws IOException;
    
        /**
         * Create a resource relative to this resource.
         * @param relativePath the relative path (relative to this resource)
         * @return the resource handle for the relative resource
         * @throws IOException if the relative resource cannot be determined
         */
        Resource createRelative(String relativePath) throws IOException;
    
        /**
         * Determine a filename for this resource, i.e. typically the last
         * part of the path: for example, "myfile.txt".
         * <p>Returns {@code null} if this type of resource does not
         * have a filename.
         */
        String getFilename();
    
        /**
         * Return a description for this resource,
         * to be used for error output when working with the resource.
         * <p>Implementations are also encouraged to return this value
         * from their {@code toString} method.
         * @see Object#toString()
         */
        String getDescription();
    
    }
    public interface InputStreamSource {
    
        /**
         * Return an {@link InputStream}.
         * <p>It is expected that each call creates a <i>fresh</i> stream.
         * <p>This requirement is particularly important when you consider an API such
         * as JavaMail, which needs to be able to read the stream multiple times when
         * creating mail attachments. For such a use case, it is <i>required</i>
         * that each {@code getInputStream()} call returns a fresh stream.
         * @return the input stream for the underlying resource (must not be {@code null})
         * @throws IOException if the stream could not be opened
         * @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
         */
        InputStream getInputStream() throws IOException;
    }
    
    public interface ResourceLoader {
    
        /** Pseudo URL prefix for loading from the class path: "classpath:" */
        String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    
    
        /**
         * Return a Resource handle for the specified resource.
         * The handle should always be a reusable resource descriptor,
         * allowing for multiple {@link Resource#getInputStream()} calls.
         * <p><ul>
         * <li>Must support fully qualified URLs, e.g. "file:C:/test.dat".
         * <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
         * <li>Should support relative file paths, e.g. "WEB-INF/test.dat".
         * (This will be implementation-specific, typically provided by an
         * ApplicationContext implementation.)
         * </ul>
         * <p>Note that a Resource handle does not imply an existing resource;
         * you need to invoke {@link Resource#exists} to check for existence.
         * @param location the resource location
         * @return a corresponding Resource handle
         * @see #CLASSPATH_URL_PREFIX
         * @see org.springframework.core.io.Resource#exists
         * @see org.springframework.core.io.Resource#getInputStream
         */
        Resource getResource(String location);
    
        /**
         * Expose the ClassLoader used by this ResourceLoader.
         * <p>Clients which need to access the ClassLoader directly can do so
         * in a uniform manner with the ResourceLoader, rather than relying
         * on the thread context ClassLoader.
         * @return the ClassLoader (only {@code null} if even the system
         * ClassLoader isn't accessible)
         * @see org.springframework.util.ClassUtils#getDefaultClassLoader()
         */
        ClassLoader getClassLoader();
    
    }
    
    DefaultResourceLoader#getResource

    (1) 检查路径是否以classpath:开头->ClassPathResource
    (2) a.通过Url来定位资源,构造UrlResource;b.构造ClassPathResource

    FileSystemResourceLoader

    修改了(2)b构造FileSystemResource

    ResourcePatternResolver

    ResourceLoader的扩展,添加了getResources
    它的实现会委派一个ResouceLoader来加载资源

    ApplicationContext

    继承了ResourcePatternResolver,它的实现一般会继承一个AbstractApplicationContext,这个类会继承DefaultReosurceLoader,而且有一个ResourcePatternResolver的对象,通过自己构造。详见Spring揭秘p91。

    相关文章

      网友评论

          本文标题:BeanFactory和ApplicationContext

          本文链接:https://www.haomeiwen.com/subject/jzasxxtx.html