SpringBoot到Spring源码分析之关键接口设计思想

本章重点是罗列Spring中BeanFactory和FactoryBean、BeanFactoryPostProcessor和BeanPostProcessor等关键接口设计和使用,了解这些接口有助于我们后续复杂源码的抽象分析

前面对SpringBoot启动过程的部分细节进行了源码分析,我们还要继续,但是再往下继续前有2点备注:

1、SpringBoot启动时除了触发Spring的加载外,还会通过我们配置Starter激活其它组件,例如SpringMVC,但是这块要到Starter分析时才连带讲解,所以在后续源码分析过程中先不要着急思考SpringMVC框架相关的逻辑。

2、SpringBoot启动过程前期涉及的源码还是比较简单的,但是越往后越复杂,涉及Spring IOC和AOP的核心逻辑。在漫长的源码分析过程中不可能每一行代码都拿出来讲一遍,所以这些细节最终还是由读者自行配合实际源码去连带关联分析,所以这就要求你必须了解Spring的一些关键知识点的设计思路,这样你才有足够的能力去独立做关联性分析。

 

 

BeanFactory和FactoryBean接口

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

 

下面看个简单例子:

 

编写MyFactoryBean类,它实现FactoryBean接口:

@Component
public class MyFactoryBean implements FactoryBean<User> {


//实现FactoryBean接口,这里定义User这个Bean的创建过程和方式
    @Override
    public User getObject() throws Exception {
        User user = new User();
        user.setName("这是通过FactoryBean实例化出来的");
        return user;
    }


//实现FactoryBean接口,这里定义当前工厂Bean需要实例化具体Bean的Class
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

User类代码:

public class User {




    private String name;


    public String getName() {
        return name;
    }


    public User setName(String name) {
        this.name = name;
        return this;
    }
}

测试代码:

@SpringBootApplication
public class SourcecodeAnalysisSpringbootApplication {


    public static void main(String[] args) {


        //启动完成后返回的应用上下文对象
        ConfigurableApplicationContext context =
                SpringApplication.run(SourcecodeAnalysisSpringbootApplication.class, args);


        //获取BeanFactory
        BeanFactory beanFactory = context.getBeanFactory();


        //通过BeanFactory从容器中获取name为myFactoryBean的Bean
        //注意这个Bean就是实现了FactoryBean接口的Bean,它定义了真正Bean的实例化和Class
        User user = (User) beanFactory.getBean("myFactoryBean");


        //或者 User user = beanFactory.getBean(User.class);


        //验证
        System.out.println(user.getName());


    }


}

控制台打印输出:

 

InitializingBean接口

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

 

下面看个简单的例子:

 

编写AccountBean类,它实现InitializingBean接口:

@Component
public class AccountBean implements InitializingBean {


    private String account;


    @Override
    public void afterPropertiesSet() throws Exception {


System.out.println("afterPropertiesSet");


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


    public String getAccount() {
        return account;
    }


    public AccountBean setAccount(String account) {
        this.account = account;
        return this;
    }
}

测试代码:

@SpringBootApplication
public class SourcecodeAnalysisSpringbootApplication {


    public static void main(String[] args) {


        testInitializingBean(args);
    }


    private static void testInitializingBean(String[] args) {
        //启动完成后返回的应用上下文对象
        ConfigurableApplicationContext context =
                SpringApplication.run(SourcecodeAnalysisSpringbootApplication.class, args);


        //获取BeanFactory
        BeanFactory beanFactory = context.getBeanFactory();




        AccountBean bean = beanFactory.getBean(AccountBean.class);


        //验证
        System.out.println(bean.getAccount());
    }
}

控制台打印输出:

 

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()之后执行。

 

下面看个简单例子:

 

编写MyBeanPostProcessor类,它实现了BeanPostProcessor接口:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {


        if (bean instanceof AccountBean) {
            System.out.println("postProcessBeforeInitialization");
        }


        return null;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {


        if (bean instanceof AccountBean) {
            System.out.println("postProcessAfterInitialization");
        }


        return null;
    }




}

其它类代码参考前面。

控制台打印结果如下:

编写MyBeanFactoryPostProcessor类,它实现BeanFactoryPostProcessor接口:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {


        for (String s : beanFactory.getBeanDefinitionNames()) {
            System.out.println(s);
        }
    }
}

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所依赖的,这点需要注意,在后续源码分析过程中也会有所体现。

OrderedPriorityOrdered接口

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());
    }


}

Dubbo源码提供的demo服务提供者xml配置文件如下:

<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

---------------------- 正文结束 ------------------------

长按扫码关注微信公众号

Java软件编程之家

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值