自定义Starter:myQuartz-spring-boot-starter 模仿学习Spring Boot starter写一个关于Quartz的自动配置的依赖。
1. 自定义Starter的模块结构 
2. Maven 依赖配置 自定义starter命名方式:
官方 spring-boot-starter-模块名 
非官方(如我们自己编写的) 模块名-spring-boot-starter 
 
spring-boot-configuration-processor是2.x必须引入的包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <?xml version="1.0" encoding="UTF-8"?> <project  xmlns ="http://maven.apache.org/POM/4.0.0"           xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >     <modelVersion > 4.0.0</modelVersion >      <groupId > com.littlefxc.examples</groupId >      <artifactId > myQuartz-spring-boot-starter</artifactId >      <version > 1.0-snapshot</version >      <packaging > jar</packaging >      <parent >          <groupId > org.springframework.boot</groupId >          <artifactId > spring-boot-starter-parent</artifactId >          <version > 1.5.18.RELEASE</version >          <relativePath  />      </parent >      <properties >          <quartz.version > 2.3.0</quartz.version >      </properties >      <dependencies >                   <dependency >              <groupId > org.springframework.boot</groupId >              <artifactId > spring-boot-starter</artifactId >          </dependency >          <dependency >              <groupId > org.springframework.boot</groupId >              <artifactId > spring-boot-configuration-processor</artifactId >              <optional > true</optional >          </dependency >                   <dependency >              <groupId > org.quartz-scheduler</groupId >              <artifactId > quartz</artifactId >              <version > ${quartz.version}</version >              <exclusions >                  <exclusion >                      <artifactId > c3p0</artifactId >                      <groupId > com.mchange</groupId >                  </exclusion >                  <exclusion >                      <artifactId > HikariCP-java6</artifactId >                      <groupId > com.zaxxer</groupId >                  </exclusion >              </exclusions >          </dependency >          <dependency >              <groupId > org.springframework</groupId >              <artifactId > spring-context-support</artifactId >          </dependency >          <dependency >              <groupId > org.springframework</groupId >              <artifactId > spring-tx</artifactId >          </dependency >          <dependency >              <groupId > org.springframework.boot</groupId >              <artifactId > spring-boot-starter-jdbc</artifactId >          </dependency >      </dependencies >  </project > 
3. 创建自动配置类QuartzAutoConfiguration 构造函数隐式注入 @Autowired, 例如:
1 2 3 4 5 6 7 8 @Service public  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? 
上文讲过构造函数隐式注入 ,但其有个缺点那就是强依赖。org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency()
** 用到的几个和Starter相关的注解:
@ConditionalOnClass,当classpath下发现该类的情况下进行自动配置。@ConditionalOnMissingBean,当Spring Context中不存在该Bean时。@EnableConfigurationProperties(QuartzProperties.class),使@ConfigurationProperties注解生效。@ConfigurationProperties,主要用来把properties配置文件转化为bean。@AutoConfigureAfter,自动注入该类在什么类加载之后。 
自定义Quartz的调度器工厂Bean的自动配置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 @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<SchedulerFactoryBeanCustomizer> customizers;     private  final  QuartzProperties properties;     private  final  JobDetail[] jobDetails;     private  final  Map<String, Calendar> calendars;     private  final  Trigger[] triggers;     private  final  ApplicationContext applicationContext;     private  final  DataSource dataSource;     private  final  PlatformTransactionManager transactionManager;     public  QuartzAutoConfiguration (QuartzProperties properties,                                     ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers,                                    ObjectProvider<JobDetail[]> jobDetails,                                    ObjectProvider<Map<String, Calendar>> calendars,                                    ObjectProvider<Trigger[]> triggers,                                    ApplicationContext applicationContext,                                    ObjectProvider<DataSource> dataSource,                                    ObjectProvider<PlatformTransactionManager> 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();     }          @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);         }                  if  (properties.getJobStoreType() == JobStoreType.JDBC) {             if  (dataSource != null ) {                 schedulerFactoryBean.setDataSource(dataSource);             }             if  (transactionManager != null ) {                 schedulerFactoryBean.setTransactionManager(transactionManager);             }         }         customize(schedulerFactoryBean);         return  schedulerFactoryBean;     }          private  void  customize (SchedulerFactoryBean schedulerFactoryBean)           if  (this .customizers != null ) {             for  (SchedulerFactoryBeanCustomizer customizer : this .customizers) {                 customizer.customize(schedulerFactoryBean);             }         }     }     private  Properties asProperties (Map<String, String> source)           Properties properties = new  Properties();         properties.putAll(source);         return  properties;     } } 
1 2 org.springframework.boot.autoconfigure.EnableAutoConfiguration =\ com.littlefxc.examples.spring.boot.autoconfigure.QuartzAutoConfiguration 
5. 其它 QuartzProperties 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @ConfigurationProperties("spring.quartz") public  class  QuartzProperties           private  JobStoreType jobStoreType = JobStoreType.MEMORY;          private  final  Map<String, String> properties = new  HashMap<>();     public  JobStoreType getJobStoreType ()           return  this .jobStoreType;     }     public  void  setJobStoreType (JobStoreType jobStoreType)           this .jobStoreType = jobStoreType;     }     public  Map<String, String> getProperties ()           return  this .properties;     } } 
JobStoreType 1 2 3 4 5 6 7 8 9 10 11 12 13 public  enum  JobStoreType           MEMORY,          JDBC } 
AutoSchedulerJobFactory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @FunctionalInterface public  interface  SchedulerFactoryBeanCustomizer           void  customize (SchedulerFactoryBean schedulerFactoryBean)  } 
6. 测试 6.1 项目结构 新建项目,结构如下图所示:
6.2 Maven 依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <?xml version="1.0" encoding="UTF-8"?> <project  xmlns ="http://maven.apache.org/POM/4.0.0"           xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >     <modelVersion > 4.0.0</modelVersion >      <groupId > com.littlefxc.examples</groupId >      <artifactId > test-autoconfigure</artifactId >      <version > 1.0-snapshot</version >      <parent >          <groupId > org.springframework.boot</groupId >          <artifactId > spring-boot-starter-parent</artifactId >                   <version > 1.5.18.RELEASE</version >          <relativePath  />      </parent >      <properties >          <quartz.version > 2.3.0</quartz.version >      </properties >      <dependencies >          <dependency >              <groupId > org.springframework.boot</groupId >              <artifactId > spring-boot-starter-web</artifactId >          </dependency >          <dependency >              <groupId > com.littlefxc.examples</groupId >              <artifactId > myQuartz-spring-boot-starter</artifactId >              <version > 1.0-snapshot</version >          </dependency >          <dependency >              <groupId > org.quartz-scheduler</groupId >              <artifactId > quartz-jobs</artifactId >              <version > ${quartz.version}</version >          </dependency >          <dependency >              <groupId > org.springframework.boot</groupId >              <artifactId > spring-boot-starter-test</artifactId >              <scope > test</scope >          </dependency >          <dependency >              <groupId > org.projectlombok</groupId >              <artifactId > lombok</artifactId >          </dependency >          <dependency >              <groupId > mysql</groupId >              <artifactId > mysql-connector-java</artifactId >          </dependency >          <dependency >              <groupId > com.alibaba</groupId >              <artifactId > druid-spring-boot-starter</artifactId >              <version > 1.1.10</version >          </dependency >      </dependencies >  </project > 
6.3 application.properties 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 logging.level.root =warn logging.level.com.littlefxc.examples =debug spring.datasource.url =jdbc:mysql://localhost:3306/learn-quartz?useSSL=false spring.datasource.username =root spring.datasource.password =123456 spring.datasource.driver-class-name =com.mysql.jdbc.Driver spring.datasource.type =com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.filters =slf4j,wall spring.datasource.druid.initial-size =1 spring.datasource.druid.min-idle =1 spring.datasource.druid.max-active =8 spring.datasource.druid.max-wait =60000 spring.datasource.druid.time-between-eviction-runs-millis =60000 spring.datasource.druid.min-evictable-idle-time-millis =300000 spring.datasource.druid.test-while-idle =true spring.datasource.druid.test-on-borrow =false spring.datasource.druid.test-on-return =false spring.datasource.druid.pool-prepared-statements =true spring.datasource.druid.max-pool-prepared-statement-per-connection-size =20 spring.quartz.job-store-type =jdbc spring.quartz.properties.org.quartz.scheduler.instanceName =schedulerFactoryBean spring.quartz.properties.org.quartz.scheduler.instanceId =AUTO spring.quartz.properties.org.quartz.scheduler.instanceIdGenerator.class =com.littlefxc.examples.CustomQuartzInstanceIdGenerator spring.quartz.properties.org.quartz.threadPool.threadCount =20 spring.quartz.properties.org.quartz.jobStore.class =org.quartz.impl.jdbcjobstore.JobStoreTX spring.quartz.properties.org.quartz.jobStore.driverDelegateClass =org.quartz.impl.jdbcjobstore.StdJDBCDelegate spring.quartz.properties.org.quartz.jobStore.useProperties =true spring.quartz.properties.org.quartz.jobStore.misfireThreshold =60000 spring.quartz.properties.org.quartz.jobStore.tablePrefix =qrtz_ spring.quartz.properties.org.quartz.jobStore.isClustered =true spring.quartz.properties.org.quartz.plugin.shutdownHook.class =org.quartz.plugins.management.ShutdownHookPlugin spring.quartz.properties.org.quartz.plugin.shutdownHook.cleanShutdown =TRUE 
6.4 代码 核心代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 @Slf4j @SpringBootApplication public  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 );         };     }          @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;     }          @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;     }          @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
1 2 3 4 5 6 7 8 9 10 11 12 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);         }     } }