-
Notifications
You must be signed in to change notification settings - Fork 701
作业开发与Spring和SpringBoot的集成
业务代码工程,大多通过Spring或SpringBoot框架来构建。
原先提供的demo,是需要在每个作业类中重写getObject方法,其做的逻辑是实例化Spring或SpringBoot,并且从Spring上下文中获取作业实例。这样做有两个弊端:
每个类需要重写getObject方法,代码冗余; 初始化Spring或SpringBoot的时机是在初始化作业时,无法及时知道代码是否OK。 另外,某些业务也存在不合理使用的情况,比如将实例化Spring或SpringBoot的代码放在作业执行时的代码块(handleJavaJob或handleMsgJob)中,这样更加延迟了初始化的时机。
1 在saturn-job-api提供一个SaturnApplication接口
package com.vip.saturn.job.application;
public interface SaturnApplication {
void init();
void destroy();
}
2 新增saturn-spring模块
该模块依赖spring,并提供支持spring的SaturnApplication实现类,具体有如下文件:
package com.vip.saturn.job.spring;
import com.vip.saturn.job.application.SaturnApplication;
public interface SpringSaturnApplication extends SaturnApplication {
<J> J getJobInstance(Class<J> jobClass);
}
package com.vip.saturn.job.spring;
import org.springframework.context.ApplicationContext;
public abstract class AbstractSpringSaturnApplication implements SpringSaturnApplication {
protected ApplicationContext applicationContext;
@Override
public <J> J getJobInstance(Class<J> jobClass) {
return applicationContext != null ? applicationContext.getBean(jobClass) : null;
}
}
package com.vip.saturn.job.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class GenericSpringSaturnApplication extends AbstractSpringSaturnApplication {
private static final String[] CONFIG_LOCATIONS_DEFAULT = {"applicationContext.xml"};
@Override
public void init() {
if (applicationContext != null) {
destroy();
}
applicationContext = run();
}
@Override
public void destroy() {
if (applicationContext != null) {
if (applicationContext instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) applicationContext).close();
}
applicationContext = null;
}
}
/**
* If the Spring container defaults aren’t to your taste, you can instead customize it
* @return the running ApplicationContext
*/
protected ApplicationContext run() {
return new ClassPathXmlApplicationContext(getConfigLocations());
}
/**
* You can override this method, to load the custom xml files. The <code>applicationContext.xml</code> will be loaded by default.
* @return array of resource locations
*/
protected String[] getConfigLocations() {
return CONFIG_LOCATIONS_DEFAULT;
}
}
3 新增saturn-springboot模块
该模块依赖saturn-spring和springboot,并提供支持springboot的SaturnApplication实现类,具体有如下文件:
package com.vip.saturn.job.springboot;
import com.vip.saturn.job.spring.AbstractSpringSaturnApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
public class GenericSpringBootSaturnApplication extends AbstractSpringSaturnApplication {
@Override
public void init() {
if (applicationContext != null) {
destroy();
}
applicationContext = run();
}
@Override
public void destroy() {
if (applicationContext != null) {
SpringApplication.exit(applicationContext);
applicationContext = null;
}
}
/**
* If the SpringApplication defaults aren’t to your taste, you can instead customize it
* @return the running ApplicationContext
*/
protected ApplicationContext run() {
return SpringApplication.run(source());
}
/**
* If you use the SpringApplication defaults, maybe you could override this method to load the source
* @return the source to load
*/
protected Object source() {
return this.getClass();
}
}
4 新增saturn-embed-spring模块
该模块依赖saturn-embed和saturn-spring,并提供支持spring和springboot环境下的SaturnApplication实现类,具体有如下文件:
package com.vip.saturn.embed.spring;
import com.vip.saturn.embed.EmbeddedSaturn;
import com.vip.saturn.job.spring.AbstractSpringSaturnApplication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
public class EmbeddedSpringSaturnApplication extends AbstractSpringSaturnApplication implements ApplicationListener {
// use spring log style
protected final Log logger = LogFactory.getLog(getClass());
private EmbeddedSaturn embeddedSaturn;
private boolean ignoreExceptions;
@Override
public void init() {
}
@Override
public void destroy() {
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
try {
if (event instanceof ContextRefreshedEvent) {
ContextRefreshedEvent contextRefreshedEvent = (ContextRefreshedEvent) event;
applicationContext = contextRefreshedEvent.getApplicationContext();
if (embeddedSaturn == null) {
embeddedSaturn = new EmbeddedSaturn();
embeddedSaturn.setSaturnApplication(this);
embeddedSaturn.start();
}
} else if (event instanceof ContextClosedEvent) {
if (embeddedSaturn != null) {
embeddedSaturn.stopGracefully();
embeddedSaturn = null;
}
}
} catch (Exception e) {
logger.warn("exception happened on event: " + event, e);
if (!ignoreExceptions) {
throw new RuntimeException(e);
}
}
}
public boolean isIgnoreExceptions() {
return ignoreExceptions;
}
public void setIgnoreExceptions(boolean ignoreExceptions) {
this.ignoreExceptions = ignoreExceptions;
}
}
5 修改saturn-embed模块,提供对SaturnApplication的支持
6 业务工程的使用方式
6.1 集成Spring
a. 依赖saturn-spring
<dependency>
<groupId>com.vip.saturn</groupId>
<artifactId>saturn-spring</artifactId>
<version>${saturn.version}</version>
</dependency>
b. 创建saturn.properties文件,定义app.class为SaturnApplication的spring实现类,既可以使用默认的GenericSpringSaturnApplication,也可自定义实现类。
app.class=com.vip.saturn.demo.Application
c. 自定义的实现类继承GenericSpringSaturnApplication
package com.vip.saturn.demo;
import com.vip.saturn.job.spring.GenericSpringSaturnApplication;
import org.springframework.context.ApplicationContext;
public class Application extends GenericSpringSaturnApplication {
/**
* If the Spring container defaults aren’t to your taste, you can instead customize it
* @return the running ApplicationContext
*/
@Override
protected ApplicationContext run() {
return super.run();
}
/**
* You can override this method, to load the custom xml files. The <code>applicationContext.xml</code> will be loaded by default.
* @return array of resource locations
*/
@Override
protected String[] getConfigLocations() {
return super.getConfigLocations();
}
}
d. 运行mvn clean compile saturn:run 启动
6.2 集成SpringBoot
a. 依赖saturn-springboot
<dependency>
<groupId>com.vip.saturn</groupId>
<artifactId>saturn-springboot</artifactId>
<version>${saturn.version}</version>
</dependency>
b. 创建saturn.properties文件,定义app.class为SaturnApplication的springboot实现类,既可以使用默认的GenericSpringBootSaturnApplication,也可自定义实现类。
app.class=com.vip.saturn.demo.Application
c. 自定义的实现类继承GenericSpringSaturnApplication
package com.vip.saturn.demo;
import com.vip.saturn.job.springboot.GenericSpringBootSaturnApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Application extends GenericSpringBootSaturnApplication {
/**
* If the SpringApplication defaults aren’t to your taste, you can instead customize it
* @return the running ApplicationContext
*/
@Override
protected ApplicationContext run() {
return super.run();
}
/**
* If you use the SpringApplication defaults, maybe you could override this method to load the source
* @return the source to load
*/
@Override
protected Object source() {
return super.source();
}
}
d. 运行mvn clean compile saturn:run 启动
6.3 SpringBoot环境中嵌入式使用Saturn
a. 依赖saturn-embed-spring
<dependency>
<groupId>com.vip.saturn</groupId>
<artifactId>saturn-embed-spring</artifactId>
<version>${saturn.version}</version>
</dependency>
b. 注册EmbeddedSpringSaturnApplication
package com.vip.saturn.demo;
import com.vip.saturn.embed.spring.EmbeddedSpringSaturnApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public EmbeddedSpringSaturnApplication embeddedSpringSaturnApplication() {
EmbeddedSpringSaturnApplication embeddedSpringSaturnApplication = new EmbeddedSpringSaturnApplication();
embeddedSpringSaturnApplication.setIgnoreExceptions(false);
return embeddedSpringSaturnApplication;
}
}
c. 使用Main方法运行,或者springboot插件启动
7 Executor对SaturnApplication的支持
- 如果是嵌入式启动,SaturnApplication由外部传入;否则,读取saturn.properties中的app.class属性值,来实例化;
- 注册job watcher前,调用SaturnApplication的init方法,来初始化业务;
- 初始化作业时,调用SaturnApplication是SpringSaturnApplication,则调用其getJobInstance方法,来获取作业实例,如果获取不到,则还是按照原来的逻辑获取;
- 优雅退出时,调用SaturnApplication的destroy方法。
注意的是:
- saturn.properties在业务代码中,只能存在一个文件,存在多个时,报错、启动失败;
- SaturnApplication只调用一次init和destroy方法,生命周期不随reinitialize变化,只在优雅退出时调用destroy方法,防止业务没有处理好bean的destroy逻辑,造成内存泄漏等问题。