关于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(); } }
启动后控制台的输出结果
1回答
是的非常感谢小伙伴提出疑问,这个问题首先解释一下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
相似问题