Springboot集成Quartz和RabbitMQ时设置为不自动运行

使用SpringBoot 2集成Quartz和RabbitMQ是一件非常简单非常方便的事情,甚至可以达到了零配置运行,大大方便了我们的使用。但是过于智能化也带来了一个问题,就是在控制的时候不太好控制。比如我的项目里集成了Quartz,但是需要部署到不同的服务器上,其中一个服务器需要运行Quartz,另一个服务器则不要运行Quartz。众所周知,像Quartz和RabbitMQ这类框架,在集成到SpringBoot之后, 都是会自动运行的,但是不可能要我打包不同的配置到不同的服务器上吧。

经过查看代码,发现此类自运行的框架都是实现了spring的SmartLifecycle接口,详看:Spring SmartLifecycle 在容器所有bean加载和初始化完毕执行,发现有个方法:

   /**
     * 根据该方法的返回值决定是否执行start方法。
* 返回true时start方法会被自动执行,返回false则不会。 */ public boolean isAutoStartup() { // 默认为false return true; }

这个方法就是关键了。根据解释,当此方法返回false时,不会自动执行start()方法。

1.首先查看Quartz。说到Quartz,肯定从核心类看起。我们知道任务的启动与暂停都跟QuartzScheduler有关,所以先从QuartzScheduler的start()方法看起。通过debug,往上一直找到SchedulerFactoryBean

	//---------------------------------------------------------------------
	// Implementation of SmartLifecycle interface
	//---------------------------------------------------------------------

	@Override
	public void start() throws SchedulingException {
		if (this.scheduler != null) {
			try {
				startScheduler(this.scheduler, this.startupDelay);
			}
			catch (SchedulerException ex) {
				throw new SchedulingException("Could not start Quartz Scheduler", ex);
			}
		}
	}

可以看到是实现了SmartLifecycle接口。那就简单了,直接去找isAutoStartup方法,看到是返回了autoStartup字段。

	@Override
	public boolean isAutoStartup() {
		return this.autoStartup;
	}

刚好看到了

	public void setAutoStartup(boolean autoStartup) {
		this.autoStartup = autoStartup;
	}

大功告成,只要找到SchedulerFactoryBean,然后调用setAutoStartup方法就行了。

下面是springboot2集成quartz的SchedulerFactoryBean配置代码

/**
 * @Description 对SchedulerFactoryBean的配置。
 * @Author yangpeng
 * @Date 2018/9/18
 **/
@Component
public class SchedulerConfig implements SchedulerFactoryBeanCustomizer {

    @Value("${mongcent.init.task}")
    private boolean runTask = false;

    @Override
    public void customize(SchedulerFactoryBean schedulerFactoryBean) {
        //如果项目不运行quartz定时任务的话,就不自动运行了
        if (!runTask) {
            schedulerFactoryBean.setAutoStartup(false);
        }
        schedulerFactoryBean.setStartupDelay(10);
        schedulerFactoryBean.setOverwriteExistingJobs(true);
    }
}

2.查看RabbitMQ。首先找到RabbitMQ的启动方法,查资料可以知道是RabbitListenerEndpointRegistry.start()方法启动MQ,RabbitListenerEndpointRegistry.stop()方法关闭MQ。

	@Override
	public void start() {
		for (MessageListenerContainer listenerContainer : getListenerContainers()) {
			startIfNecessary(listenerContainer);
		}
	}

	/**
	 * Start the specified {@link MessageListenerContainer} if it should be started
	 * on startup or when start is called explicitly after startup.
	 * @param listenerContainer the container.
	 * @see MessageListenerContainer#isAutoStartup()
	 */
	private void startIfNecessary(MessageListenerContainer listenerContainer) {
		if (this.contextRefreshed || listenerContainer.isAutoStartup()) {
			listenerContainer.start();
		}
	}

可以看到,是MessageListenerContainer类中的isAutoStartup方法决定是否要运行MQ,listenerContainer.isAutoStartup()的值从哪里来呢?我们来看一下MessageListenerContainer的初始化:在start()方法里看到是来自getListenerContainers()方法。

	/**
	 * @return the managed {@link MessageListenerContainer} instance(s).
	 */
	public Collection getListenerContainers() {
		return Collections.unmodifiableCollection(this.listenerContainers.values());
	}

再查找this.listenerContainers是从哪里来的

	/**
	 * Create a message listener container for the given {@link RabbitListenerEndpoint}.
	 * 

This create the necessary infrastructure to honor that endpoint * with regards to its configuration. *

The {@code startImmediately} flag determines if the container should be * started immediately. * @param endpoint the endpoint to add. * @param factory the {@link RabbitListenerContainerFactory} to use. * @param startImmediately start the container immediately if necessary * @see #getListenerContainers() * @see #getListenerContainer(String) */ @SuppressWarnings("unchecked") public void registerListenerContainer(RabbitListenerEndpoint endpoint, RabbitListenerContainerFactory factory, boolean startImmediately) { Assert.notNull(endpoint, "Endpoint must not be null"); Assert.notNull(factory, "Factory must not be null"); String id = endpoint.getId(); Assert.hasText(id, "Endpoint id must not be empty"); synchronized (this.listenerContainers) { Assert.state(!this.listenerContainers.containsKey(id), "Another endpoint is already registered with id '" + id + "'"); MessageListenerContainer container = createListenerContainer(endpoint, factory); this.listenerContainers.put(id, container); if (StringUtils.hasText(endpoint.getGroup()) && this.applicationContext != null) { List containerGroup; if (this.applicationContext.containsBean(endpoint.getGroup())) { containerGroup = this.applicationContext.getBean(endpoint.getGroup(), List.class); } else { containerGroup = new ArrayList(); this.applicationContext.getBeanFactory().registerSingleton(endpoint.getGroup(), containerGroup); } containerGroup.add(container); } if (startImmediately) { startIfNecessary(container); } } }

看到下面这两行代码了吗

MessageListenerContainer container = createListenerContainer(endpoint, factory);
this.listenerContainers.put(id, container);

在createListenerContainer方法看到是从RabbitListenerContainerFactory.createListenerContainer方法生成的。

MessageListenerContainer listenerContainer = factory.createListenerContainer(endpoint);

看一下实现类,在AbstractRabbitListenerContainerFactory中,看到配置了一大堆属性,其中就有我们关心的autoStartup,返回的是 this.autoStartup

		else if (this.autoStartup != null) {
			instance.setAutoStartup(this.autoStartup);
		}

很自然的,就找到了set方法

	/**
	 * @param autoStartup true for auto startup.
	 * @see AbstractMessageListenerContainer#setAutoStartup(boolean)
	 */
	public void setAutoStartup(Boolean autoStartup) {
		this.autoStartup = autoStartup;
	}

那么,如何才能调用AbstractRabbitListenerContainerFactory中的setAutoStartup方法呢?有没有觉得这个类名有点眼熟,这是一个抽象类,我们去看一下有哪些子类。一看,看到了SimpleRabbitListenerContainerFactory,这不就是我们配置消费者属性的那个配置类吗,完美!直接调用setAutoStartup方法就解决了

最后给出配置。

    @Bean(name = "myRabbitListenerContainer")
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        //如果项目没有
        if (!runMq) {
            //是否自动启动
            factory.setAutoStartup(false);
        }
        return factory;
    }

 

结语:转了一大圈,不过就是在配置里面加上两行代码而已,似乎没什么用。但看了这一圈,让我对spring的启动过程有了更多的认识,同时学习了优秀框架的一些设计思维,这是一件非常值得去做的事

你可能感兴趣的:(framework)