【Spring源码研究-01】spring一些核心类及功能概述

 

目录

一、BeanDefinition

二、BeanDefinitionReader

三、AnnotatedBeanDefinitionReader

四、ClassPathBeanDefinitionScanner

扩展点:

五、BeanFactory

六、ApplicationContext

国际化

资源加载

获取运行时环境

事件发布

七、AnnotationConfigApplicationContext

八、类型转化

PropertyEditor

ConversionService

TypeConverter

九、BeanPostProcessor

十、BeanFactoryPostProcessor

十一、FactoryBean


先对一些基础的类以及概念有个初步的了解,建立一个宏观的认知,后面进入spring源码的处理逻辑再慢慢细化研究其功能与扩展点。

spring版本:spring-framework-5.0.x

一、BeanDefinition

 

BeanDefinition接口源码

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

 

  1. BeanDefinition接口规定了,BeanDefinition对象必须的一些功能,具体的实现由子类完成。
  2. BeanDefinition对象是对spring中的bean的一个定义
  3. spring会先将@Bean 、@Component 等标注的类解析成一个BeanDefinition对象。
  4. 后面创建对象都是根据BeanDefinition对象中的属性进行创建。

 

可以类比java中的Class对象,但是BeanDefinition对象的描述要更加的复杂,并且里面的规则只适用于spring框架。

 

 

二、BeanDefinitionReader

 

BeanDefinitionReader接口源码

public interface BeanDefinitionReader {

 

这个接口的主要功能是将某些资源,加载解析成对应的BeanDefinition对象然后保存到BeanFactory对象中。

 

BeanDefinitionReader接口的主要实现类是XmlBeanDefinitionReader

 

XmlBeanDefinitionReader 类源码

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

这个类主要是加载spring.xml等xml配置文件,将文件中配置的<bean>标签数据,解析成对应的BeanDefinition对象。

 

来个案例:

测试类:

public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //创建XmlBeanDefinitionReader对象,加载指定的xml文件,将里面的<bean>标签解析成BeanDefinition对象
        //将解析后的BeanDefinition对象注册到context对象中
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
        //返回解析成功的个数
        int i = reader.loadBeanDefinitions("spring.xml");
        System.out.println(i);
    }

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byType">
    <bean id="a" class="com.zzl.lg.demo1.A">
    </bean>
</beans>

A类

public class A {
}

 

三、AnnotatedBeanDefinitionReader

 

AnnotatedBeanDefinitionReader类源码:

public class AnnotatedBeanDefinitionReader {

    private final BeanDefinitionRegistry registry;

    private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

    private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

    private ConditionEvaluator conditionEvaluator;

 

这个类的作用是:

  • 直接将指定的某个没有使用spring提供的@Bean 、@Component 等注解的类解析成BeanDefinition对象并保存到容器中
  • 如果被解析的类上有spring的其他注解,也会被解析。

 

 

案例:

 

测试类:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //创建AnnotatedBeanDefinitionReader对象,将指定的类加载成BeanDefinition对象
        //将解析后的BeanDefinition对象注册到context对象中
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context);
        //解析指定的类
        reader.register(A.class);
        //刷新容器
        context.refresh();
        //获取A对象
        System.out.println(context.getBean(A.class));
    }

A类

@Scope
public class A {
}

 

 

四、ClassPathBeanDefinitionScanner

 

 

ClassPathBeanDefinitionScanner类源码:

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

    private final BeanDefinitionRegistry registry;

ClassPathBeanDefinitionScanner类主要的作用是扫描指定包路径下的 @Component 注解,将有这个注解的类解析成BeanDefinition对象并放入容器中

 

案例:

 

测试类:

  public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //创建ClassPathBeanDefinitionScanner对象,将指定包路径下标注有@Component注解的类解析成BeanDefinition对象
        //将解析后的BeanDefinition对象注册到context对象中
        ClassPathBeanDefinitionScanner reader = new ClassPathBeanDefinitionScanner(context);
        //扫描指定的包路径
        reader.scan("com.zzl.lg.demo1");
        //刷新容器
        context.refresh();
        //获取A对象
        System.out.println(context.getBean(A.class));
    }

A类


import org.springframework.stereotype.Component;
@Component
public class A {
}

 

//扫描指定的包路径
reader.scan("com.zzl.lg.demo1");

这行代码里面的逻辑就是,扫描指定路径下的所有.class文件,并且将标有@Component 注解的类解析出来

 

扩展点:

这里有一个扩展点就是,让自己的自定义注解也可以拥有@Component注解相同的作用被spring扫描成BeanDefinition对象

 

具体的源码逻辑以及如何扩展,后面会深入研究

 

 

五、BeanFactory

 

BeanFactory接口源码:

public interface BeanFactory {

 

这是一个spring容器的顶级接口

 

他有一个非常重要的实现类 DefaultListableBeanFactory

DefaultListableBeanFactory类源码

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

DefaultListableBeanFactory类的结构关系图:

 

image.png

大图地址:https://www.processon.com/view/link/5fafa01ae401fd7d35c98c0e

 

 

DefaultListableBeanFactory实现或者继承了许多的类,那么自然会具备很多的功能,我们大概来了解一下他都具备些什么功能:

 

AliasRegistry接口:别名注册器

  • 可以为某个名字,注册对应的别名
  • 注册别名的方法 void registerAlias(String name, String alias);

 

SimpleAliasRegistry类:实现了AliasRegistry接口

  • 具体的实现了注册别名的逻辑,根据源码可以看出别名可以同时设置多个
  • 核心源码:this.aliasMap.put(alias, name); 可以看出map中的key是别名,那么就可以实现多个别名对应一个name

 

BeanDefinitionRegistry接口:主要功能是注册BeanDefinition对象

BeanFactory:bean工厂

  • 提供根据名字,类型等或者bean对象的方法

 

SingletonBeanRegistry接口:注册和获取单例bean对象

DefaultSingletonBeanRegistry类:实现了SingletonBeanRegistry接口

  • 可以直接注册对象到单例池中
  • 核心代码:this.singletonObjects.put(beanName, singletonObject);
  • 这里思考一个问题:我们添加到单例池的对象是否走完了spring的生命周期

 

HierarchicalBeanFactory接口:主要是获取父bean工厂的功能

  • 继承了BeanFactory接口

ListableBeanFactory接口:在BeanFactory的基础上,增加了一些针对bean的操作或查询方法

  • 获取所有BeanDefinition的beanNames
  • 根据类型获取所有的bean等一些功能

 

FactoryBeanRegistrySupport:支持了FactoryBean的功能

ConfigurableBeanFactory:在HierarchicalBeanFactory和SingletonBeanRegistry的基础上再次增加功能

  • 添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、
  • 设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、
  • 设置类型转化服务(表示该BeanFactory可以进行类型转化)、
  • 可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),
  • 可以合并BeanDefinition,
  • 可以销毁某个Bean等等功能

AutowireCapableBeanFactory:是直接继承了BeanFactory

  • 在BeanFactory的基础上,支持在创建Bean的过程中能对Bean进行自动装配

AbstractBeanFactory类:实现了ConfigurableBeanFactory接口,继承了FactoryBeanRegistrySupport,

  • 这个BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames

ConfigurableListableBeanFactory接口:

  • 继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory
  • 没有提供新的功能,主要是整合上面三个接口

AbstractAutowireCapableBeanFactor类:

  • 继承了AbstractBeanFactory,
  • 实现了AutowireCapableBeanFactory,
  • 拥有了自动装配的功能

DefaultListableBeanFactory:

  • 继承了AbstractAutowireCapableBeanFactory,
  • 实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,
  • 所以DefaultListableBeanFactory的功能很强大很全面

 

 

直接使用DefaultListableBeanFactory对象的功能示例

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 注册BeanDefinition
beanFactory.registerBeanDefinition("user", beanDefinition);
// 注册别名
beanFactory.registerAlias("user", "user1");
// 注册BeanPostProcessor
beanFactory.addBeanPostProcessor(new LubanBeanPostProcessor());

// 获取Bean对象
System.out.println(beanFactory.getBean("user1"));
// 根据类型获取beanNames
System.out.println(beanFactory.getBeanNamesForType(User.class));

 

 

六、ApplicationContext

 

ApplicationContext接口源码:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, 
        HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

 

ApplicationContext是个接口,可以把它理解为一个特殊的BeanFactory

 

ApplicationContext接口的关系图:

image.png

大图地址:https://www.processon.com/view/link/5fafc0397d9c08138ec727e9

 

 

ApplicationContext大概具备哪些功能:

  • HierarchicalBeanFactory:拥有获取父BeanFactory的功能
  • ListableBeanFactory:拥有获取beanNames的功能
  • ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
  • EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
  • ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
  • MessageSource:拥有国际化功能

 

 

一些功能示例:

 

国际化

先定义一个MessageSource:

@Bean
public MessageSource messageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("messages");
    return messageSource;
}

 

注:配置文件的命名规则是: 前缀_后缀 (messages_sn_en)

 

 

国际化配置文件:

messages.properties

name=tom

messages_sn_en.properties

name=汤姆

测试类:

public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        String message = context.getMessage("name", null, new Locale("sn_en"));
        System.out.println(message);
    }

 

资源加载

ApplicationContext还拥有资源加载的功能,比如,可以直接利用ApplicationContext获取某个文件的内容:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Resource resource = context.getResource("file://D:\\IdeaProjects\\spring-framework\\luban\\src\\main\\java\\com\\luban\\entity\\User.java");
System.out.println(resource.contentLength());

你可以想想,如果你不使用ApplicationContext,而是自己来实现这个功能,就比较费时间了。

 

还比如你可以:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Resource resource = context.getResource("classpath:com/luban/entity/User.class");
System.out.println(resource.contentLength());

 

还可以一次性获取多个:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Resource[] resources = context.getResources("classpath:com/luban/service/*.class");
for (Resource resource : resources) {
    System.out.println(resource.contentLength());
}

 

这个功能用到了策略模式

获取运行时环境

 

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// 获取JVM所允许的操作系统的环境
context.getEnvironment().getSystemEnvironment();

// 获取JVM本身的一些属性,包括-D所设置的
context.getEnvironment().getSystemProperties();

// 还可以直接获取某个环境或properties文件中的属性
context.getEnvironment().getProperty("yyy")

注意,可以利用

@PropertySource("classpath:application.properties")

来使得某个properties文件中的参数添加到运行时环境中

 

 

事件发布

先定义一个事件监听器

@Bean
public ApplicationListener applicationListener() {
    return new ApplicationListener() {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            System.out.println("接收到了一个事件");
        }
    };
}

 

然后发布一个事件:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
context.publishEvent("kkk");

 

 

七、AnnotationConfigApplicationContext

 

AnnotationConfigApplicationContext类源码:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

    private final AnnotatedBeanDefinitionReader reader;

    private final ClassPathBeanDefinitionScanner scanner;

AnnotationConfigApplicationContext类的结构图:

image.png

 

大图地址:https://www.processon.com/view/link/5fafcae007912964bbad2918

 

 

AnnotationConfigApplicationContext类的一些功能介绍:

 

  • AnnotationConfigRegistry接口:
    • 可以单独注册某个为类为BeanDefinition(可以处理该类上的@Configuration注解,已经可以处理@Bean注解
    • 同时可以扫描指定包路径下的类文件,并将符合条件的类封装成BeanDefinition对象
  • ConfigurableApplicationContext接口:继承了ApplicationContext接口,
    • 增加了,添加事件监听器、
    • 添加BeanFactoryPostProcessor、
    • 设置Environment,
    • 获取ConfigurableListableBeanFactory等功能
  • AbstractApplicationContext类:实现了ConfigurableApplicationContext接口
  • GenericApplicationContext类:继承了AbstractApplicationContext,实现了BeanDefinitionRegistry接口,
    • 拥有了所有ApplicationContext的功能,
    • 并且可以注册BeanDefinition,
    • 注意这个类中有一个属性(DefaultListableBeanFactory beanFactory)
  • AnnotationConfigApplicationContext类:
    • 继承了GenericApplicationContext,
    • 实现了AnnotationConfigRegistry接口,
    • 拥有了以上所有的功能

 

 

八、类型转化

PropertyEditor

JDK中提供的类型转化工具类

 

public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        User user = new User();
        user.setName(text);
        this.setValue(user);
    }
}

测试

StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
propertyEditor.setAsText("1");
User value = (User) propertyEditor.getValue();
System.out.println(value);

 

如何向Spring中注册PropertyEditor:

@Bean
public CustomEditorConfigurer customEditorConfigurer() {
    CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
    Map<Class<?>, Class<? extends PropertyEditor>> propertyEditorMap = new HashMap<>();
    propertyEditorMap.put(User.class, StringToUserPropertyEditor.class);
    customEditorConfigurer.setCustomEditors(propertyEditorMap);
    return customEditorConfigurer;
}

 

假设现在有如下Bean:

@Component
public class UserService {

    @Value("true")
    User test;

    public void test() {
        System.out.println(test);
    }
}

那么test属性就能正常的完成属性赋值

 

 

ConversionService

Spring中提供的类型转化服务,它比PropertyEditor更强大

public class StringToUserConverter implements ConditionalGenericConverter {

    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
    }

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(String.class, User.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        User user = new User();
        user.setName((String)source);
        return user;
    }
}
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
User value = conversionService.convert("1", User.class);
System.out.println(value);

 

如何向Spring中注册ConversionService:

@Bean
public ConversionServiceFactoryBean conversionService() {
    ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
    conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));

    return conversionServiceFactoryBean;
}

 

TypeConverter

整合了PropertyEditor和ConversionService的功能,是Spring内部用的

SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
//typeConverter.setConversionService(conversionService);
User value = typeConverter.convertIfNecessary("1", User.class);
System.out.println(value);

 

九、BeanPostProcessor

Bean的后置处理器,可以在创建每个Bean的过程中进行干涉,是属于BeanFactory中一个属性

 

十、BeanFactoryPostProcessor

Bean工厂的后置处理器,是属于ApplicationContext中的一个属性,是ApplicationContext在实例化一个BeanFactory后,可以利用BeanFactoryPostProcessor继续处理BeanFactory。

 

程序员可以通过BeanFactoryPostProcessor间接的设置BeanFactory,比如上文中的CustomEditorConfigurer就是一个BeanFactoryPostProcessor,我们可以通过它向BeanFactory中添加自定义的PropertyEditor。

十一、FactoryBean

 

允许程序员自定义一个对象通过FactoryBean间接的放到Spring容器中成为一个Bean。

 

那么它和@Bean的区别是什么?因为@Bean也可以自定义一个对象,让这个对象成为一个Bean。

 

区别在于利用FactoryBean可以更加强大,因为你通过定义一个XxFactoryBean的类,可以再去实现Spring中的其他接口,比如如果你实现了BeanFactoryAware接口,那么你可以在你的XxFactoryBean中获取到Bean工厂,从而使用Bean工厂做更多你想做的,而@Bean则不行。

 

 

 

 

我大概走了一下断点,研究了一下获取一个FactoryBean对象的时候spring内部是怎么做的。有一个比较注意的点是通过FactoryBean创建的对象是没有走spring bean的生命周期的,就是直接将创建好的对象返回了。

 

 

接下来我们写个简单的示例 跟着断点大概看一下获取FactoryBean创建对象的过程。

 

测试类源码:

image.png

public class User {
}
@Component
public class MyFactoryBean implements FactoryBean {
    /**
     * 默认返回的对象是单例的  就是说这个方法只会被调用一次,
     * 之后再想获取对象是从spring缓存中获取的
     * @return
     * @throws Exception
     */
    @Override
    public Object getObject() throws Exception {
        //根据我们自己的逻辑,自定义创建对象
        User user = new User();
        return user;
    }
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}
@ComponentScan(value = "com.zzl.lg.factorybean") //扫描的包路径填你自己的包路径就可以了
@Configuration
public class MyConfig {

}

测试类

public class Aplication {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

        MyFactoryBean myFactoryBean = (MyFactoryBean) context.getBean("myFactoryBean");
        System.out.println(myFactoryBean.getObject());
    }
}

 

 

接下来我会把一些关键点的断点截图:

 

 

首先我们断点的开始位置:

image.png

我们将第一个断点打在测试类的这行代码,对象最终是getBean方法返回的,那么我们肯定就是去研究getBean方法内部的逻辑。

 

核心代码类和方法截图:

image.png

 

核心的代码逻辑就在AbstractBeanFactory类的doGetBean方法中实现的。

 

doGetBean方法中跟获取FactoryBean中自定义对象有关的代码就是方法开始的这一部分代码,其中有两处关建点,就是我写了注释的两行代码

   protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

        /*验证bean的名字是否合法*/
        /*
        * 1.判断当前传入的名字,是否是以&开头的,如果是将&截取掉,返回剩余的作为BeanName
        * 2.将1.返回的BeanName作为别名去别名池中拿,如果拿到了将拿到真实的BeanName返回,如果没有拿到将自己返回
        *
        * */
        final String beanName = transformedBeanName(name);
        Object bean;

        
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            //获取FactoryBean内部创建的对象,或者获取FactoryBean对象本身
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

 

 

其实这两行关键代码里面的逻辑都很简单,我大概说一下,相信大家自己点进去肯定是可以看懂的。

 

final String beanName = transformedBeanName(name);

这行代码里面是对我们传入的name的处理,有两个逻辑:

  • 1.首先判断是否以&开头,如果是将&去掉(可以去掉多个&知道name前面没有&为止)如果不是直接返回name
  • 2.将第一步返回的name作为别名,在别名池中去拿是否有与其对应的真实的BeanName,如果有返回BeanName没有返回自己

 

bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

 

这行代码就是返回我们最终需要的对象,这里面有两个逻辑: 

  • 返回真实的FactoryBean对象
  • 返回FactoryBean对象生成的对象

 

首先 方法中的 sharedInstance 属性就是从spring容器单例池中获取的 FactoryBean对象:

 

  • 如果你外面调用的时候传入的名字开头是以&开头的,那么直接返回 sharedInstance 对象
  • 如果你外面调用的时候传入的名字开头不是以&开头的,那么会调用sharedInstance 对象的getObject方法,返回我们自定义的对象,并将其放入使用工厂生成对象的专属的缓存池中(不是spring的单例池)

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值