【啃啊啃 Spring5 源码】细碎四:核心类总结

  • 1. 资源配置相关
      • 1.1 Resource
      • 1.2 ResourceLoader
      • 1.3 ResourcePatternResolver
  • 2. 环境属性相关
      • 2.1 PropertySource
      • 2.2 PropertySources
      • 2.3 PropertyResolver
      • 2.4 Environment
  • 3. bean配置相关
      • 3.1 BeanDefinition
      • 3.2 BeanDefinitionHolder
      • 3.3 BeanDefinitionRegistry
      • 3.4 BeanDefinitionReader
  • 4. bean相关
      • 4.1 BeanWrapper
      • 4.2 ObjectFactory
      • 4.3 FactoryBean
  • 5. 容器相关
      • 5.1 BeanFactory

阅读spring源码时,有许多 “核心类” 的作用我们了解了,才会阅读的更顺畅。这里总结下我阅读源码时认为比较重要,需要了解的一些 “核心类”

注:本文前三节 为参考【Spring4揭秘 基础2】PropertySource和Enviroment系列文章,进行的总结扩展

1. 资源配置相关

1.1 Resource

public interface Resource extends InputStreamSource {  
       URL getURL() throws IOException;  
       URI getURI() throws IOException;  
       File getFile() throws IOException;  

       …………
}  

Resource 主要是对spring中各种资源(包括文件系统资源、class路径资源、Byte、servletContext资源、url资源)的抽象,spring中xml配置文件便是通过Resource 子类读取。

它继承了InputStreamSource接口,子类通过重写getInputStream()方法,便可轻松的读取各种资源。解决了Java中不同资源读取需求写不同代码的困难。

常见子类
- ClassPathResource:对class文件的读取,默认路径为项目根路径(/target),也可指定Class或ClassLoader来获取不同路径。
- FileSystemResource:对文件系统资源的读和写,路径为文件系统路径,例如windos中:“D://a.json”
- ByteArrayResource:对byte[]数组资源的读取
- UrlResource:对url网络资源的读取
- ServletContextResource:对web工程中文件的读取。通过ServletContext读取webRoot目录下的资源
- InputStreamResource:对输入流资源的读取,直接通过给定的输入流读取。
- BeanDefinitionResource:对BeanDefinition的读取,主要通过getBeanDefinition()方法,而不是getInputStream()
【啃啊啃 Spring5 源码】细碎四:核心类总结_第1张图片

1.2 ResourceLoader

public interface ResourceLoader {
     /**
      * 根据给定的路径,返回不同的Resource。例如路径中包含“classpath:”,则返回ClassPathResource
       */
    Resource getResource(String location);
}

从源码也可以看出,ResourceLoader其实就是Resource的工厂类。根据资源位置 “location” 的不同,返回不同的Resource供使用者读取资源。

常见子类
- DefaultResourceLoaderResourceLoader的基本功能实现类。返回Resource的策略为:location中包含“classpath:”,则返回ClassPathResource;包含“/”或出现异常返回ClassPathContextResource;其他情况返回UrlResource
- FileSystemResourceLoader:继承DefaultResourceLoader,重写getResourceByPath()方法,返回文件资源加载类FileSystemContextResource
- ServletContextResourceLoader:继承DefaultResourceLoader,重写getResourceByPath()方法,返回servletContext资源加载类ServletContextResource
【啃啊啃 Spring5 源码】细碎四:核心类总结_第2张图片

1.3 ResourcePatternResolver

public interface ResourcePatternResolver extends ResourceLoader {

    /**
     * 多resource匹配location 前缀
     */
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    /**
     * 根据给定路径返回多个资源
     */
    Resource[] getResources(String locationPattern) throws IOException;
}

ResourcePatternResolver继承ResourceLoader接口,增加了getResources()方法用于根据匹配多个资源返回。例如对于路径:“classpath*:*.xml” 将匹配class路径下的所有的xml文件,加载成对应Resource后返回。

常见子类
- PathMatchingResourcePatternResolver:接口的基础功能实现类,从类名也可看出,可解析通配符路径(如“application*.xml”)来返回Resources
- ServletContextResourcePatternResolver:解析webRoot目录下的通配符路径,返回Resources
- ApplicationContext:spring中的容器接口也继承了ResourcePatternResolver接口,代表spring中的所有容器都有根据通配符路径获取资源的功能
【啃啊啃 Spring5 源码】细碎四:核心类总结_第3张图片

2. 环境属性相关

2.1 PropertySource

public abstract class PropertySource {
    protected final String name;//属性源名称
    protected final T source;//属性源(包含键值对的对象,如Properties、Map、ServletContext等)
    public String getName();  //获取属性源的名字  
    public T getSource();        //获取属性源  
    public boolean containsProperty(String name);  //是否包含某个属性  
    public abstract Object getProperty(String name);   //得到属性名对应的属性值   
} 

PropertySource 代表包含若干键值对(key-value)的数据源(source)。这个源可以是PropertiesMapServletContext等。在spring中,读取的propertie配置、servlet中的环境参数、乃至JDK系统参数、系统变量,都会以PropertySource的形式存在。

常见子类
- MapPropertySource:source为一个Map,也就是键值对存在Map中
- PropertiesPropertySource:从名字也可以看出,该类代表properties文件中键值对。继承MapPropertySource,source为Properties对象(Properties继承了HashMap,所以PropertiesPropertySource也是一个MapPropertySource)。
- SystemEnvironmentPropertySource:继承MapPropertySource,source为Map对象,代表系统中的环境变量,如JAVA_HOME、MABEN_HOME之类。 与MapPropertySource不同的是,取值时它将会忽略大小写,”.”和”_”将会转化
- ResourcePropertySource:source为Resource,代表spring中resource资源(例如:“/com/myco/foo.properties”、“file:/path/to/file.xml”)中的键值对属性
- ServletContextPropertySource:source为ServletContext,代表web环境中的servlet上下文中的参数
【啃啊啃 Spring5 源码】细碎四:核心类总结_第4张图片

2.2 PropertySources

public interface PropertySources extends Iterable> {

    /**
     * 返回是否包含该名称的PropertySource
     */
    boolean contains(String name);

    /**
     *返回指定名称的PropertySource
     */
    @Nullable
    PropertySource get(String name);

}

PropertySources即多个PropertySource,可看成PropertySource集合。

常见子类
- MutablePropertySources:PropertySources接口的默认也是唯一实现类,内部维护了一个PropertySource的集合(CopyOnWriteArrayList实现)。支持向集合的头部、末尾、及指定位置添加PropertySource

2.3 PropertyResolver

public interface PropertyResolver {  

    //是否包含某个属性  
    boolean containsProperty(String key);  

    //获取属性值 如果找不到返回null   
    String getProperty(String key);  

    //获取属性值,如果找不到返回默认值        
    String getProperty(String key, String defaultValue);  

    //获取指定类型的属性值,找不到返回null  
    <T> T getProperty(String key, Class<T> targetType);  

    //获取指定类型的属性值,找不到返回默认值  
    <T> T getProperty(String key, Class<T> targetType, T defaultValue);  

    //获取属性值为某个Class类型,找不到返回null,如果类型不兼容将抛出ConversionException  
    <T> Class<T> getPropertyAsClass(String key, Class<T> targetType);  

    //获取属性值,找不到抛出异常IllegalStateException  
    String getRequiredProperty(String key) throws IllegalStateException;  

    //获取指定类型的属性值,找不到抛出异常IllegalStateException         
    <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;  

    //替换文本中的占位符(${key})到属性值,找不到不解析  
    String resolvePlaceholders(String text);  

    //替换文本中的占位符(${key})到属性值,找不到抛出异常IllegalArgumentException  
    String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;  
}  

实现PropertyResolver接口的子类,一般拥有了PropertySources的成员变量,该接口主要用于从PropertySources中获取对应属性。

从接口的resolvePlaceholders()方法可以看出,该接口还有解析文本,将文本中占位符转换为PropertySources对应属性的功能。spring配置文件中的占位符最后便是这样被替换成对应的属性。

2.4 Environment

public interface Environment extends PropertyResolver {  
        //得到当前激活的配置环境 
    String[] getActiveProfiles();  

        //得到默认激活的配置环境
    String[] getDefaultProfiles();  

        //是否接受某些配置环境
    boolean acceptsProfiles(String... profiles);  
}  

Environment接口继承了PropertyResolver接口,主要添加了“激活配置环境”功能。

在实际开发中,一般维护了多套开发环境,例如:dev、test、uat、prodcut。每种开发环境的配置是有差别的,spring中实现了多环境配置的功能:

如下,定义了test、dev两个配置环境,它们加载的bean并不相同,启动spring时,可指定active profile来加载指定配置环境下的bean

    <beans profile="test">
        <bean class="test.wsz.spring.aop.AspectDemo" />
        <bean id="a" class="test.wsz.spring.bean.A" >
            <constructor-arg value="wsz1" index="0" />
        bean>
    beans>

    <beans profile="dev">
        <bean id="b" class="test.wsz.spring.bean.B" />
        <bean class="test.wsz.spring.postProcess.MyBeanPostProcessor" />
    beans>

常见子类
- AbstractEnvironmentEnvironment的具体功能实现类
- StandardEnvironment:标准环境类,会自动注册System.getProperties()中的属性到环境中。spring应用启动时采用该环境类。
- StandardServletEnvironment:标准servlet环境类,web应用时采用。

【啃啊啃 Spring5 源码】细碎四:核心类总结_第5张图片

3. bean配置相关

spring中bean对象的生成可以分成两步:
1. 读取配置文件中bean的配置,将其转换为bean配置类:BeanDefinition(持有beanName、beanClass、properties等信息)
2. 根据BeanDefinition中的配置信息,实例化并初始化bean

本节主要介绍第一步,bean配置相关的类。

3.1 BeanDefinition

ublic interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    //单例或原型
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; 
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

     //Bean角色
    int ROLE_APPLICATION = 0; 
    int ROLE_SUPPORT = 1; 
    int ROLE_INFRASTRUCTURE = 2;

    // 返回/设置父BeanDefinition 
    String getParentName(); 
    void setParentName(String parentName);

    //返回/设置 当前的BeanClassName(不等于最终Bean的名称)
    String getBeanClassName(); 
    void setBeanClassName(String beanClassName);

    //返回设置 factory bean name  
    String getFactoryBeanName(); 
    void setFactoryBeanName(String factoryBeanName);

    String getFactoryMethodName(); 
    void setFactoryMethodName(String factoryMethodName);

    //返回/设置 bean的Scope:单例、原型等等
    String getScope(); 
    void setScope(String scope);

    //返回/设置 bean是否懒加载
    boolean isLazyInit(); 
    void setLazyInit(boolean lazyInit);

    String[] getDependsOn(); 
    void setDependsOn(String... dependsOn);

    boolean isAutowireCandidate(); 
    void setAutowireCandidate(boolean autowireCandidate);

    boolean isPrimary(); 
    void setPrimary(boolean primary);

    ConstructorArgumentValues getConstructorArgumentValues();

    //返回/设置 bean的属性
    MutablePropertyValues getPropertyValues(); 
    boolean isSingleton(); 
    boolean isPrototype(); 
    boolean isAbstract(); 
    int getRole(); 
    String getDescription(); 
    String getResourceDescription(); 
    BeanDefinition getOriginatingBeanDefinition();
}

从接口方法可以看出,BeanDefiniton主要包含了bean的名称、class、scope、属性、描述、懒加载等配置信息。

常见子类
- GenericBeanDefinition:一个综合性的标准BeanDefiniton,包含了BeanDefinition中的主要功能
- AnnotatedGenericBeanDefinition:一个注解式的标准BeanDefiniton,继承了GenericBeanDefinition,在其基础上增加了对类注解配置的支持,即可通过该类获取注解配置。
- ConfigurationClassBeanDefinition:如果spring中的配置是用类来表示的,则解析后生成的bean配置类为该类

【啃啊啃 Spring5 源码】细碎四:核心类总结_第6张图片

3.2 BeanDefinitionHolder

public class BeanDefinitionHolder implements BeanMetadataElement {

    private final BeanDefinition beanDefinition;

    private final String beanName;

    @Nullable
    private final String[] aliases;

    public boolean matchesName(@Nullable String candidateName) {
        ……省略
    }
}

从类代码中可以看出,BeanDefinitionHolder其实就是一个持有了beanDefinition和bean名称beanName,及bean的别名数组aliases的类。

matchesName()方法可以知道,该类能够在beanDefinition注册时,匹配占位符,如果匹配中则注册该BeanDefinition

3.3 BeanDefinitionRegistry

public interface BeanDefinitionRegistry extends AliasRegistry {

     // 注册一个BeanDeinition
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;

     // 删除注册的BeanDeinition
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    //获取注册的BeanDefinition
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    boolean containsBeanDefinition(String beanName);

    String[] getBeanDefinitionNames();

    int getBeanDefinitionCount();

    boolean isBeanNameInUse(String beanName);

}

BeanDefinitionRegistry接口定义了‘注册/获取BeanDefinition’的方法。实现该接口的子类一般都维护了一个BeanDeinition集合:Map beanDefinitionMap

常见子类
- DefaultListableBeanFactory:这个类是spring容器功能的核心类,它实现了BeanDefinitionRegistry接口,代表spring中的beanFactory基本都有注册、删除BeanDefinition的功能
- GenericApplicationContext:从类名也可以看出,该类是spring上下文的主要功能实现类,该类对BeanDefinition的注册、删除,实际是委托DefaultListableBeanFactory来实现
- SimpleBeanDefinitionRegistryBeanDefinitionRegistry接口功能的简单实现,主要用来测试功能的……

【啃啊啃 Spring5 源码】细碎四:核心类总结_第7张图片

3.4 BeanDefinitionReader

public interface BeanDefinitionReader {
    //获取BeanDefinition的注册器
    BeanDefinitionRegistry getRegistry();

    //获取用于加载配置的ResourceLoader(上文有提到)
    ResourceLoader getResourceLoader();

    //获取用于加载bean class的ClassLoader
    ClassLoader getBeanClassLoader();

    BeanNameGenerator getBeanNameGenerator();

        //加载解析配置,并注册解析后的BeanDefinition
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

        //加载解析多个配置,并注册解析后的BeanDefinition
    int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

    //根据配置路径,加载解析配置,并注册解析后的BeanDefinition
    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

    //根据多个配置路径,加载解析多个配置,并注册解析后的BeanDefinition
    int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;

}

BeanDefinitionReader接口的方法可以看出,这个接口主要实现了 直接从配置中加载并注册所有BeanDefinition的功能,注册的动作实际委托了BeanDefinitionRegister实现。

常见子类
- AbstractBeanDefinitionReaderBeanDefinitionReader接口的主要功能实现抽象类
- PropertiesBeanDefinitionReader:用于从properties文件中加载注册BeanDefinition(ps:我也是第一次知道,properties文件也可以配置bean,配置方式可见该类注释)
- XmlBeanDefinitionReader:用于从xml文件中加载注册BeanDefinition
【啃啊啃 Spring5 源码】细碎四:核心类总结_第8张图片

扩展
上面的BeanDefinitionReader子类都是从配置文件中加载注册beanDefinition,但是实际应用中,我们经常采用注解方式来声明一个bean。那这中注解bean又是如何被发现并注册的呢?

注解式bean的加载注册,主要看两个类:
- AnnotatedBeanDefinitionReader:该类并没有实现BeanDefinitonReader接口,主要根据bean class来加载注册BeanDefinition
- ClassPathBeanDefinitionScanner:从类名也可以看出,该类主要是扫描classpath中的注解类(@Component、@Repository、@Service、@Controller),然后加载注册对应的BeanDefinition

4. bean相关

4.1 BeanWrapper

public interface BeanWrapper extends ConfigurablePropertyAccessor {

    void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);

    int getAutoGrowCollectionLimit();

    //返回被包装的bean实例
    Object getWrappedInstance();

    //返回被包装的bean的class
    Class getWrappedClass();

    //返回被包装的bean的属性集合
    PropertyDescriptor[] getPropertyDescriptors();

    //根据属性名称,返回被包装的bean的属性
    PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;

}

BeanWrapper是spring中bean的包装类的接口,从接口定义的方法可以看出,beanWrapper的子类包含bean的实例及属性信息。

它的父接口ConfigurablePropertyAccessor定义了setConversionService()方法来设置属性转换器(spring中ConversionService负责属性的转换,例如将 字符串“2018-08-20”转换为Date类型)。

ConfigurablePropertyAccessor继承的TypeConverter接口提供了属性转换的方法convertIfNecessary()。故BeanWrapper还有将bean中的属性进行转换的功能。

spring中实例化bean结束后,会将生成的BeanWrapper作为形参传给populateBean()方法,进行bean的初始化:即bean的属性设值。这个设值操作便会用到BeanWrapper的属性转换功能。

常见子类
- BeanWrapperImplBeanWrapper接口的默认实现。继承了AbstractNestablePropertyAccessor类,父类中的registerCustomEditor()方法可增加自定义的PropertyEditor属性编辑器(PropertyEditor和ConversionService一样用于属性的转换,具体可看:SpringMVC数据类型转换

【啃啊啃 Spring5 源码】细碎四:核心类总结_第9张图片

4.2 ObjectFactory

public interface ObjectFactory {

    //返回一个被工厂管理的实例
    T getObject() throws BeansException;

}

就是一个很简单工厂接口,在调用时返回实例。在【啃啊啃 Spring5 源码】细碎二:bean的循环依赖中我们看到过它的运用:

为了解决bean的循环依赖,bean在第一次获取时,生成的ObjectFactory对象,getObject()方法实际上会调用createBean()方法创建bean。而bean在创建的过程中,实例化完成后会重新创建ObjectFactory对象,getObject()方法会变成调用getEarlyBeanReference(),用于获取的缓存的实例化的bean。这样依赖bean再通过ObjectFactory工厂获取实例时,获取的便是缓存中的bean。

4.3 FactoryBean

public interface FactoryBean {

    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class getObjectType();

    default boolean isSingleton() {
        return true;
    }

}

FactoryBean对应spring中的工厂类bean。即定义的bean实际是一个工厂,可获取bean。这里不引申了,有兴趣的同学可以自行百度了解。

最后比较下ObjectFactoryFactoryBeanBeanFactory
1. ObjectFactoryFactoryBean很类似,都是工厂类,用于获取bean。区别在于ObjectFactory是在代码中根据场景手动调用getObject()方法获取bean,而FactoryBean是配置写好,spring会自动调用getObject()方法获取bean。
2. BeanFactory是容器的父类,管理着bean,并不能简单的看成工厂类,和前两者区别很大

5. 容器相关

5.1 BeanFactory

public interface BeanFactory {

    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String name) throws BeansException;

     T getBean(String name, @Nullable Class requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

     T getBean(Class requiredType) throws BeansException;

     T getBean(Class requiredType, Object... args) throws BeansException;

    boolean containsBean(String name);

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, @Nullable Class typeToMatch) throws NoSuchBeanDefinitionException;

    @Nullable
    Class getType(String name) throws NoSuchBeanDefinitionException;


    String[] getAliases(String name);

}

BeanFactory是spring容器的核心接口,spring中的容器类,都实现了该接口。定义了获取bean、获取bean类型、判断bean是否单例等基础方法。

常见子类

BeanFactory下面有三个子接口:
- HierarchicalBeanFactory:提供容器分层功能,通过getParentBeanFactory()方法获取父容器
- ListableBeanFactory:和类名一样,提供了getBeanDefinitionNames()列出容器内所有bean的名字、根据 “type” 获取所有bean名字 等方法
- AutowireCapableBeanFactory:提供了自动装配的功能,根据类定义BeanDefinition装配Bean、执行前、后处理器等。

主要接口:
- ConfigurableBeanFactory:该接口包含大量容器工厂的配置方法,从bean的注册、加载、销毁、依赖,到后处理器的添加,以及属性编辑器和类型转换……
- ConfigurableListableBeanFactoryConfigurableBeanFactory接口的补充,另外还继承了其余的beanFactory接口,基本包含了BeanFactory体系目前的所有方法,可以看成BeanFactory体系中功能最完善的接口
- ApplicationContext:spring容器的核心接口,继承了BeanFactory体系的接口,在其功能上增加了资源加载、事件体制、环境配置、国际化支持、bean生命周期管理等功能
核心类:
- DefaultListableBeanFactory:实现了ConfigurableListableBeanFactory接口,spring容器最重要的一个核心类。实现了容器的大部分功能。已经废弃的XmlBeanFactory便是继承自它。而现在我们使用的ApplicationContext容器子类,也都是组合的它来实现重要功能

【啃啊啃 Spring5 源码】细碎四:核心类总结_第10张图片

你可能感兴趣的:(JavaEE)