博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义Starter
阅读量:2168 次
发布时间:2019-05-01

本文共 14025 字,大约阅读时间需要 46 分钟。

自定义Starter:myQuartz-spring-boot-starter

模仿学习Spring Boot starter写一个关于Quartz的自动配置的依赖。

1. 自定义Starter的模块结构

模块结构.png

2. Maven 依赖配置

自定义starter命名方式:

  • 官方 spring-boot-starter-模块名
  • 非官方(如我们自己编写的) 模块名-spring-boot-starter

spring-boot-configuration-processor是2.x必须引入的包。

4.0.0
com.littlefxc.examples
myQuartz-spring-boot-starter
1.0-snapshot
jar
org.springframework.boot
spring-boot-starter-parent
1.5.18.RELEASE
2.3.0
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-configuration-processor
true
org.quartz-scheduler
quartz
${quartz.version}
c3p0
com.mchange
HikariCP-java6
com.zaxxer
org.springframework
spring-context-support
org.springframework
spring-tx
org.springframework.boot
spring-boot-starter-jdbc

3. 创建自动配置类QuartzAutoConfiguration

构造函数隐式注入

在4.3之前,如果你构造函数中要依赖另外一个bean,你必须显示依赖@Autowired, 例如:

@Servicepublic class FooService {
private final FooRepository repository; @Autowired public FooService(FooRepository repository) {
this.repository = repository }}

相当常见的用例但是如果你忘记构造函数上的@Autowired注释,容器将抛出一个寻找默认构造函数的异常,除非你在bean定义设置中明确指出autowire模式’constructor’。

因此,从4.3开始,您不再需要在这样的单构造函数场景中指定显式注入注释。对于那些根本不带任何容器注释的类来说,这是特别优雅的,FooService会从beanFactory中查找FooRepository。

同样的,@Configuration类在4.3之前不支持构造函数注入。

为什么要使用ObjectProvider?

上文讲过构造函数隐式注入,但其有个缺点那就是强依赖。

Spring Framework 4.3引入了ObjectProvider,它是现有ObjectFactory接口的扩展,具有方便的签名,例如getIfAvailable和getIfUnique,只有在它实际存在时才检索bean(可选支持)或者如果可以确定单个候选者(特别是:主要候选者)在多个匹配的bean的情况下)。它的Spring核心源码见org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency()

** 用到的几个和Starter相关的注解:

  • @ConditionalOnClass,当classpath下发现该类的情况下进行自动配置。
  • @ConditionalOnMissingBean,当Spring Context中不存在该Bean时。
  • @EnableConfigurationProperties(QuartzProperties.class),使@ConfigurationProperties注解生效。
  • @ConfigurationProperties,主要用来把properties配置文件转化为bean。
  • @AutoConfigureAfter,自动注入该类在什么类加载之后。

自定义Quartz的调度器工厂Bean的自动配置类

@Configuration@ConditionalOnClass({
Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class})@EnableConfigurationProperties(QuartzProperties.class)@AutoConfigureAfter({
DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})public class QuartzAutoConfiguration {
private final static Logger log = LoggerFactory.getLogger(QuartzAutoConfiguration.class); private final List
customizers; private final QuartzProperties properties; private final JobDetail[] jobDetails; private final Map
calendars; private final Trigger[] triggers; private final ApplicationContext applicationContext; private final DataSource dataSource; private final PlatformTransactionManager transactionManager; public QuartzAutoConfiguration(QuartzProperties properties, ObjectProvider
> customizers, ObjectProvider
jobDetails, ObjectProvider
> calendars, ObjectProvider
triggers, ApplicationContext applicationContext, ObjectProvider
dataSource, ObjectProvider
transactionManager) { this.properties = properties; this.jobDetails = jobDetails.getIfAvailable(); this.calendars = calendars.getIfAvailable(); this.triggers = triggers.getIfAvailable(); this.applicationContext = applicationContext; this.dataSource = dataSource.getIfAvailable(); this.transactionManager = transactionManager.getIfAvailable(); this.customizers = customizers.getIfAvailable(); } /** * 如果没有调度器,就创建 * * @return */ @Bean @ConditionalOnMissingBean public SchedulerFactoryBean schedulerFactoryBean() { log.info("Init SchedulerFactoryBean"); SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setJobFactory(new AutoSchedulerJobFactory(this.applicationContext.getAutowireCapableBeanFactory())); if (!this.properties.getProperties().isEmpty()) { schedulerFactoryBean.setQuartzProperties(asProperties(this.properties.getProperties())); } if (this.jobDetails != null && this.jobDetails.length > 0) { schedulerFactoryBean.setJobDetails(this.jobDetails); } if (this.calendars != null && !this.calendars.isEmpty()) { schedulerFactoryBean.setCalendars(this.calendars); } if (this.triggers != null && this.triggers.length > 0) { schedulerFactoryBean.setTriggers(this.triggers); } // todo quartz数据源初始化 if (properties.getJobStoreType() == JobStoreType.JDBC) { if (dataSource != null) { schedulerFactoryBean.setDataSource(dataSource); } if (transactionManager != null) { schedulerFactoryBean.setTransactionManager(transactionManager); } } customize(schedulerFactoryBean); return schedulerFactoryBean; } /** * 自定义调度器 * * @param schedulerFactoryBean */ private void customize(SchedulerFactoryBean schedulerFactoryBean) { if (this.customizers != null) { for (SchedulerFactoryBeanCustomizer customizer : this.customizers) { customizer.customize(schedulerFactoryBean); } } } private Properties asProperties(Map
source) { Properties properties = new Properties(); properties.putAll(source); return properties; }}

4. resources/META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.littlefxc.examples.spring.boot.autoconfigure.QuartzAutoConfiguration

5. 其它

QuartzProperties

@ConfigurationProperties("spring.quartz")public class QuartzProperties {
/** * Quartz job store type. */ private JobStoreType jobStoreType = JobStoreType.MEMORY; /** * Additional Quartz Scheduler properties. */ private final Map
properties = new HashMap<>(); public JobStoreType getJobStoreType() {
return this.jobStoreType; } public void setJobStoreType(JobStoreType jobStoreType) {
this.jobStoreType = jobStoreType; } public Map
getProperties() {
return this.properties; }}

JobStoreType

public enum JobStoreType {
/** * Store jobs in memory. */ MEMORY, /** * Store jobs in the database. */ JDBC}

AutoSchedulerJobFactory

/** * 模仿了:{@link org.springframework.boot.autoconfigure.quartz.AutowireCapableBeanJobFactory} * * @author fengxuechao * @date 12/19/2018 * @see 注入Spring上下文(applicationContext) */public class AutoSchedulerJobFactory extends SpringBeanJobFactory {
private AutowireCapableBeanFactory beanFactory; AutoSchedulerJobFactory(AutowireCapableBeanFactory factory) {
beanFactory = factory; } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); this.beanFactory.initializeBean(job, null); return job; }}

SchedulerFactoryBeanCustomizer

/** * 回调接口,可以由希望在完全初始化之前自定义Quartz SchedulerFactoryBean的bean实现,特别是调整其配置。 * @author fengxuechao */@FunctionalInterfacepublic interface SchedulerFactoryBeanCustomizer {
/** * 自定义{@link SchedulerFactoryBean}. * @param schedulerFactoryBean the scheduler to customize */ void customize(SchedulerFactoryBean schedulerFactoryBean);}

6. 测试

6.1 项目结构

新建项目,结构如下图所示:

test-autoconfigure项目结构.png

6.2 Maven 依赖

4.0.0
com.littlefxc.examples
test-autoconfigure
1.0-snapshot
org.springframework.boot
spring-boot-starter-parent
1.5.18.RELEASE
2.3.0
org.springframework.boot
spring-boot-starter-web
com.littlefxc.examples
myQuartz-spring-boot-starter
1.0-snapshot
org.quartz-scheduler
quartz-jobs
${quartz.version}
org.springframework.boot
spring-boot-starter-test
test
org.projectlombok
lombok
mysql
mysql-connector-java
com.alibaba
druid-spring-boot-starter
1.1.10

6.3 application.properties

logging.level.root=warnlogging.level.com.littlefxc.examples=debug# DataSource Configspring.datasource.url=jdbc:mysql://localhost:3306/learn-quartz?useSSL=falsespring.datasource.username=rootspring.datasource.password=123456spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.druid.filters=slf4j,wallspring.datasource.druid.initial-size=1spring.datasource.druid.min-idle=1spring.datasource.druid.max-active=8spring.datasource.druid.max-wait=60000spring.datasource.druid.time-between-eviction-runs-millis=60000spring.datasource.druid.min-evictable-idle-time-millis=300000spring.datasource.druid.test-while-idle=truespring.datasource.druid.test-on-borrow=falsespring.datasource.druid.test-on-return=falsespring.datasource.druid.pool-prepared-statements=truespring.datasource.druid.max-pool-prepared-statement-per-connection-size=20# Quartz Configspring.quartz.job-store-type=jdbcspring.quartz.properties.org.quartz.scheduler.instanceName=schedulerFactoryBeanspring.quartz.properties.org.quartz.scheduler.instanceId=AUTOspring.quartz.properties.org.quartz.scheduler.instanceIdGenerator.class=com.littlefxc.examples.CustomQuartzInstanceIdGeneratorspring.quartz.properties.org.quartz.threadPool.threadCount=20spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTXspring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegatespring.quartz.properties.org.quartz.jobStore.useProperties=truespring.quartz.properties.org.quartz.jobStore.misfireThreshold=60000spring.quartz.properties.org.quartz.jobStore.tablePrefix=qrtz_spring.quartz.properties.org.quartz.jobStore.isClustered=truespring.quartz.properties.org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPluginspring.quartz.properties.org.quartz.plugin.shutdownHook.cleanShutdown=TRUE

6.4 代码

核心代码

@Slf4j@SpringBootApplicationpublic class TestAutoConfigure {
public static void main(String[] args) {
SpringApplication.run(TestAutoConfigure.class, args); } @Bean public SchedulerFactoryBeanCustomizer dataSourceCustomizer() {
return (schedulerFactoryBean) -> {
schedulerFactoryBean.setOverwriteExistingJobs(false); schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true); schedulerFactoryBean.setStartupDelay(10); }; } /** * JobDetailFactoryBean * * @return */ @Bean public JobDetailFactoryBean jobDetailFactoryBean() {
JobDetailFactoryBean bean = new JobDetailFactoryBean(); bean.setName("job-1"); bean.setGroup("job-group-1"); bean.setJobClass(MyJob.class); JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("hello", "world"); bean.setJobDataMap(jobDataMap); bean.setDurability(true); return bean; } /** * CronTriggerFactoryBean * * @param jobDetail * @return */ @Bean public CronTriggerFactoryBean cronTrigger(JobDetail jobDetail) {
CronTriggerFactoryBean bean = new CronTriggerFactoryBean(); bean.setName("cron-1"); bean.setGroup("cron-group-1"); bean.setCronExpression("0/5 * * * * ?"); bean.setJobDetail(jobDetail); return bean; } /** * Job */ @Component public static class MyJob extends QuartzJobBean {
@Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail = jobExecutionContext.getJobDetail(); String jobName = jobDetail.getKey().getName(); String jobGroup = jobDetail.getKey().getGroup(); String jobDataMapHello = (String) jobDetail.getJobDataMap().get("hello"); log.info("job.name = {}, job.group = {}, job.dataMap.hello = {}", jobName, jobGroup, jobDataMapHello); } }}

CustomQuartzInstanceIdGenerator

public class CustomQuartzInstanceIdGenerator implements InstanceIdGenerator {
@Override public String generateInstanceId() throws SchedulerException {
try {
return UUID.randomUUID().toString().replaceAll("-", ""); } catch (Exception ex) {
throw new SchedulerException("Couldn't generate UUID!", ex); } }}

转载地址:http://hvxzb.baihongyu.com/

你可能感兴趣的文章
【LEETCODE】67-Add Binary
查看>>
【LEETCODE】7-Reverse Integer
查看>>
【LEETCODE】165-Compare Version Numbers
查看>>
【LEETCODE】299-Bulls and Cows
查看>>
【LEETCODE】223-Rectangle Area
查看>>
【LEETCODE】12-Integer to Roman
查看>>
【学习方法】如何分析源代码
查看>>
【LEETCODE】61- Rotate List [Python]
查看>>
【LEETCODE】143- Reorder List [Python]
查看>>
【LEETCODE】82- Remove Duplicates from Sorted List II [Python]
查看>>
【LEETCODE】86- Partition List [Python]
查看>>
【LEETCODE】147- Insertion Sort List [Python]
查看>>
【算法】- 动态规划的编织艺术
查看>>
用 TensorFlow 让你的机器人唱首原创给你听
查看>>
对比学习用 Keras 搭建 CNN RNN 等常用神经网络
查看>>
深度学习的主要应用举例
查看>>
word2vec 模型思想和代码实现
查看>>
怎样做情感分析
查看>>
用深度神经网络处理NER命名实体识别问题
查看>>
用 RNN 训练语言模型生成文本
查看>>