玩玩Spring(六)--Spring源码核心类&接口

Spring源码核心类&接口

Spring源码分析之关键接口设计思想,对于阅读spring源码大有裨益,同时增加对spring使用过程中的高级特性

BeanFactory和 FactoryBean 接口

这两个接口它们总是被放在一起来对比分析,原因可能是它们名字长得很相近,实际上这两个接口有着本质的区别。BeanFactory 接口定义如何获取 Spring IOC 容器中的 Bean ,而 FactoryBean 接口定义是如何实例化某个 Bean , FactoryBean 接口实现类本身被当做一个特殊的 Bean ,而 BeanFactory 接口实现类是来管理所有 Bean 的(包括 FactoryBean 接口实现类getObject返回返回的Bean)。可以简单总结: BeanFactory 接口面向的是 Bean 容器而 FactoryBean 接口面向的是单个 Bean的创建过程 。

@Data
public class User implements Serializable {
    private String name;
    private int age;
    private Book book;
    private Subs subs;

    public void initParams() {
        setName("张三2");
        setAge(29);
    }

    public void bye() {
        System.out.println("bai bai ~2");
    }

}

@Component
public class MyFactoryBean implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        User user = new User();
        user.setName("这是通过FactoryBean实例化出来的");
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

@Slf4j
public class UpTest {
    private AnnotationConfigApplicationContext context = null;

    @Before
    public void initContext() {
        log.info("初始化容器");
        context = new AnnotationConfigApplicationContext("com.haha.up");
        context.registerShutdownHook();
    }

    @After
    public void end() {
        context.close();
    }


    @Test
    public void test01() {
        BeanFactory beanFactory = context.getBeanFactory();
        User user = beanFactory.getBean(User.class);
        System.out.println(user.getName());
    }
}
  • 结果
01:13:18.213 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
01:13:18.216 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
01:13:18.219 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'myFactoryBean'
这是通过FactoryBean实例化出来的
01:13:18.219 [main] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7f9a81e8: startup date [Sat May 01 01:13:18 CST 2021]; root of context hierarchy
01:13:18.219 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
01:13:18.220 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1da51a35: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,myFactoryBean]; root of factory hierarchy

InitializingBean接口

如果某个 Bean 实现了该接口, 当该 Bean 初始化完成后 Spring 会回调该接口的唯一方法 afterPropertiesSet() ,在这个方法中你可以对该 Bean 初始化后做最终的属性检查或其它工作。很多其它框架跟 Spring 集成时就有这种需求,例如大名鼎鼎的 RPC 框架 Dubbo 便是通过实现这种接口,在回调方法 afterPropertiesSet 中进行参数检查以及服务暴露等工作的。

@Data
@Component
public class SomeBean implements InitializingBean {
    private String some;

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet");

        if (!"root".equals(some)) {
            some = "root";
        }
    }

}

@Test
    public void test02() {
        BeanFactory beanFactory = context.getBeanFactory();
        SomeBean someBean = beanFactory.getBean(SomeBean.class);
        System.out.println(someBean.getSome());
    }
  • 结果
01:17:13.591 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'someBean'
root
01:17:13.591 [main] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7f9a81e8: startup date [Sat May 01 01:17:13 CST 2021]; root of context hierarchy

BeanFactoryPostProcessor和 BeanPostProcessor 接口

分析Spring 源码不得不提这两个 PostProcessor 接口,其中 Spring AOP 就是借助 BeanPostProcessor 接口来实现切面字节码代理的,重要性可见一斑。

BeanFactoryPostProcessor 是 BeanFactory 初始化完成后的后处理器钩子接口

BeanPostProcessor 是 Bean 初始化完成后的后处理器钩子接口。

当回调BeanFactoryPostProcessor 接口的唯一钩子函数时,此时的状态是 BeanFactory 对象已经实例化完成且所有的 BeanDefinition 加载完成。但是需要注意,在这个接口中可以修改 BeanDefinition 对象封装的一些信息或者修改 Bean 容器的排序或者注册额外新的 BeanDefinition 等动作,但不建议去获取 Bean 实例(调 getBean 方法)并操作Bean实例,这样会因为过早实例化 Bean 而违反 Spring 容器内部的流程导致意外的错误。如果要对 Bean 实例进行操作建议通过实现 BeanPostProcessor 接口来完成而不是 BeanFactoryPostProcessor 接口。

BeanPostProcessor 接口有两个实现方法,分别是 postProcessBeforeInitialization(…) 和 postProcessAfterInitialization(…) ,设计两个方法的意义在于处理类似同时实现了 InitializingBean 接口时的回调时间点。从名字上很容易看出 postProcessBeforeInitialization 在 afterPropertiesSet() 之前执行,而 postProcessAfterInitialization 在 afterPropertiesSet() 之后执行。

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof SomeBean) {
            System.out.println("postProcessBeforeInitialization");
        }
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof SomeBean) {
            System.out.println("postProcessAfterInitialization");
        }
        return null;
    }
}

Aware接口

这个接口 在Spring框架内部有很多子接口,例如:

  • ApplicationContextAware

  • BeanNameAware

  • MessageSourceAware

  • ResourceLoaderAware

  • ApplicationEventPublisherAware

等等,这些接口名称以及接口方法签名的命名非常统一,都是类型下面这样:

public interface ApplicationContextAware extends Aware {
   //都是通过提供一个setXXX,其中XXX跟截取Aware后的接口名称相同
   void setApplicationContext(ApplicationContext applicationContext) 
   throws BeansException;
}

它的作用是提供某个对象数据接口,当某个Bean 实现带 Aware 后缀的接口时, Spring 会在适当的时候将对应的对象提供给你用。例如当某个 Bean 实现 ApplicationContextAware 接口时,当 ApplicationContext 构建完毕时就会回调setApplicationContext 方法提供给你用,这里要注意的是具体什么时候提供给你由 Spring 框架内部决定。由于有些接口要等到 Spring 基本加载完成才能提供给你,所以当 Bean 实现了某些接口时,这个 Bean 是不能通过 @Autowired 注解被别的 Bean 所依赖的,这点需要注意。

Ordered和PriorityOrdered接口

PriorityOrdered 接口继承 Ordered 接口,可以通过实现 PriorityOrdered 或Ordered 接口来指定 Bean 或 PostProcessor 初始化和执行顺序。另外也可以通过注解 Order 的方式来指定,它们的效果和级别是相同的。

ApplicationListener接口

它是Spring 框架内部基础接口,必须和 ApplicationEvent 类配套使用,它主要是定义各种场景的不同阶段事件进行发布和订阅的设计。

NamespaceHandler接口

这个接口在SpringBoot 以前也就是使用 xml 配置项目时非常重要,它对于其他框架集成到 Spring 来说是最常要实现的一个接口,它的作用是用于解析自定义 xsd 命名空间 xml 配置文件,将实现的 NamespaceHandler 配置到 classpath:META-INF/spring.handlers 文件中即可被 Spring 所识别。
通常配合Spring 提供的抽象类 NamespaceHandlerSupport#registerBeanDefinitionParser(String, BeanDefinitionParser) 来使用,你需要编写不同类型 Bean 的解析器 BeanDefinitionParser ,并将对应解析器注册到 NamespaceHandlerSupport 中,这样 Spring 就可以在需要时调用到你自定义的解析类将 BeanDefinition 解析出来进行后续 Spring 容器加载流程。

下面给出Dubbo 实现自定义 xsd 配置文件不同类型 Bean 的 BeanDefinitionParser 注册到 NamespaceHandlerSupport 类的源码作为例子:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {


    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }


    @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }


}
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">


    <!-- provider's application name, used for tracing dependency relationship -->
    <dubbo:application name="demo-provider"/>


    <!-- use multicast registry center to export service -->
    <dubbo:registry address="multicast://224.5.6.7:1234"/>


    <!-- use dubbo protocol to export service on port 20880 -->
    <dubbo:protocol name="dubbo" port="20880"/>


    <!-- service implementation, as same as regular local bean -->
    <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>


    <!-- declare the service interface to be exported -->
    <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/>


</beans>

其中我们要关注的是xml 配置文件指定的命名空间: xmlns:dubbo=“http://dubbo.apache.org/schema/dubbo” 以及命名空间的 element 元素,它对应到前面 registerBeanDefinitionParser (…) 方法的 第一个字符串参数。

spring.handlers文件源码如下:

http://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler

总结

学会了这些,在看spring源码的时候就不会疯疯癫癫了,真香!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你是真的皮005

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值