关于BeanDefinitionBuilder的setInitMethodName

来源:3-21 定时任务通用组件封装-5

0x0wh04m1

2020-05-10 23:49:01

老师,您好!首先表示一下肯定,在这部分确实学到很多封装的知识,受益匪浅!

这里有些疑问想提一下,就是在编写ElasticJobConfParser这个类的时候,BeanDefinitionBuilder调用setInitMethodName设置初始化方法的名称为init,然后最后又手动去调用SpringJobScheduler的init方法,这样不是会执行两次init方法吗?

git上ElasticJobConfParser类的源码地址:https://git.imooc.com/class-73/Architect-Stage-5-RabbitMQ/src/master/rabbit-parent/rabbit-task/src/main/java/com/bfxy/rabbit/task/parser/ElasticJobConfParser.java

部分代码

...
// 126行
BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(SpringJobScheduler.class);
factory.setInitMethodName("init");
factory.setScope("prototype");
...
SpringJobScheduler scheduler = (SpringJobScheduler)applicationContext.getBean(registerBeanName);
scheduler.init();
// 156行

为此我自己写了个简单的demo来验证了这个设想

要放入Spring容器的类

package com.springboot.springbootdemo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author: wh0am1
 * @date: 2020/5/10
 * @description:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private Integer id;

    private String name;

    public void init() {
        System.err.println("------------ init -------------");
        System.err.println(id + " : " + name);
    }
}

实现了ApplicationListener接口的类

package com.springboot.springbootdemo.demo;

import com.springboot.springbootdemo.entity.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @author: wh0am1
 * @date: 2020/5/10
 * @description:
 */
@Component
public class BeanDefinitionDemo implements ApplicationListener<ApplicationReadyEvent> {

    /**
     * Handle an application event.
     *
     * @param event the event to respond to
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(User.class);
        builder.setInitMethodName("init");
        builder.setScope("prototype");
        builder.addConstructorArgValue(1);
        builder.addConstructorArgValue("bean definition");
        builder.setLazyInit(false);
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        beanFactory.registerBeanDefinition("user", builder.getBeanDefinition());
        User user = applicationContext.getBean("user", User.class);
        user.init();
    }
}

启动后控制台的输出结果

http://img.mukewang.com/climg/5eb8226409df292523120404.jpg

写回答

1回答

Java架构师讲师团

2020-05-11

是的非常感谢小伙伴提出疑问,这个问题首先解释一下getBean方法内部会做一次createbean的init操作,最后的scheduler.init() 确实是可以省略的,不过esjob里面的init是单例模型可以忽略此问题不计,所以我这里显示的init调用也不会有大问题,但是如果非单例情况则需要注意此问题~ ,因为之前生产环境出现过不加载的问题,所以这里我还是显示调用一次,而且当当的esjob解决此问题也是通过单例来屏蔽该问题的发生的(这是早期的一个问题),这就小孩儿没娘。。说来话长了,哈哈 在这里不延伸太细节;

详见1:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean方法

详见2:com.dangdang.ddframe.job.lite.api.JobScheduler#init


1
0x0wh04m1
h 谢谢老师的答疑,又学到了~
h020-05-11
共1条回复

Java架构师-技术专家

千万级电商项目从0到100全过程,覆盖Java程序员不同成长阶段的核心问题与解决方案

2672 学习 · 5839 问题

查看课程