preface

As programmers with architectural aspirations, customizing Springboot-starter is a skill we must master. Many projects in the enterprise will have their own requirements to encapsulate the starter. This is also the interview question I was asked when I went out for an interview at the end of 2019. At that time, as a young man who had just graduated for six months, I only knew how to use the official production, but I did not realize it myself. I hope this article will be helpful to those who can’t make starter yet

What is Springboot-starter & how does it work

We all know that one of the core reasons why SpringBoot is so popular is that it provides a series of starter scenarios, which are packaged with various open source components and can be used by introducing dependencies. Recall the RedisTemplate for Redis, the RabbitTemplate for RabbitMQ, etc. Have you ever wondered why you can configure some properties in application.yml so that you can inject them directly into your application and use these template classes?

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;
Copy the code

In fact, the principle is relatively easy to understand. Take the automatic configuration of RedisTemplate as an example. If you read the source code of RedisAutoConfiguration class, you will find that, simply speaking, the relevant configuration in application. Set these configurations into the RedisTemplate, and then inject the RedisTemplate object into the Spring container. That way, when we need it, we can just grab it from the Spring container.

The benefits of using starter

Address, IP, port, etc. in the configuration file, and then write a configuration class to accept these attributes and set them to the XxlJobSpringExecutor object. The XxlJobSpringExecutor object is then injected into the Spring container.

@configuration public class XxlJobConfig {@value ("${xxl.job.admin.addresses}") // Read the Value in the Configuration file private String adminAddresses; /** * insert other attributes omit... * */ / inject XxlJobSpringExecutor into spring@bean public XxlJobSpringExecutor xxlJobExecutor() {XxlJobSpringExecutor executor = new XxlJobSpringExecutor(); executor.setAdminAddresses(adminAddresses); executor.setAppname(appname); executor.setAddress(address); executor.setIp(ip); executor.setPort(port); executor.setAccessToken(accessToken); executor.setLogPath(logPath); executor.setLogRetentionDays(logRetentionDays); return executor; }}Copy the code

Writing a scheduled Task

@xxlJob ("demoJobHandler") public void demoJobHandler() throws Exception {/** * Service ellipsis..... / * *}Copy the code

This is a clear violation of the DRY (Don’t Repeat Yourself) principle of having to write the code again for each project with XXL-job. So we can write this code in the starter. If we need xxl-job in the starter, we can configure the related properties in the configuration file. The great thing about starter is that it keeps us from writing repetitive code! XxL-job-springboot-starter

Create a xxl-job-springboot-starter

Initialize the starter project structure

Start by using Spring Initializer to create a project and write out maven’s coordinates and package name

Then delete the files you don’t need and create a meta-INF /spring.factories file with the following directory structure

Then add two dependencies to pom.xml

<! --xxl-job dependency --> <dependency> <groupId>com. Xuxueli </groupId> <artifactId> The < version > 2.3.0 < / version > < / dependency > <! Introducing this dependency, <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>Copy the code

Write automatic configuration code

The XxlJobProperties class accepts the configuration in application.yml, using the @ConfigurationProperties annotation provided by SpringBoot, and specifying the configuration prefix in the YML file. You don’t have to write an EL expression assignment with @Value anymore

@ConfigurationProperties(prefix = XxlJobProperties.PREFIX) @Data public class XxlJobProperties { public static final String PREFIX = "xxl-job"; /** * private Admin Admin = new Admin(); /** * private Executor Executor = new Executor(); /** * Actuator communication TOKEN [optional] : non-null enabled */ private String accessToken; / /... Omit}Copy the code

Then write an XxlJobAutoConfiguration class to read the configuration and inject the XxlJobSpringExecutor object into the Spring container.

@Configuration(proxyBeanMethods = false) @ConditionalOnClass(IJobHandler.class) @ EnableConfigurationProperties (XxlJobProperties. Class) public class XxlJobAutoConfiguration {/ * * * * * / reserved initialization and destruction method @Bean(initMethod = "start", DestroyMethod = "destroy") /** * this will be injected into Spring ** / @conditionAlonmissingBean public if XxlJobExecutor is not injected XxlJobExecutor xxlJobExecutor(XxlJobProperties xxlJobProperties, ObjectProvider<XxlJobExecutorCustomizer> customizers) { XxlJobExecutor xxlJobExecutor = new XxlJobSpringExecutor(); / / dispatch center configuration xxlJobExecutor. SetAdminAddresses (xxlJobProperties. GetAdmin () getAddresses ()); / / actuator configuration xxlJobExecutor. SetAppname (xxlJobProperties. GetExecutor () getAppName ()); xxlJobExecutor.setIp(xxlJobProperties.getExecutor().getIp()); xxlJobExecutor.setPort(xxlJobProperties.getExecutor().getPort()); xxlJobExecutor.setAccessToken(xxlJobProperties.getAccessToken()); xxlJobExecutor.setLogPath(xxlJobProperties.getExecutor().getLogPath()); xxlJobExecutor.setLogRetentionDays(xxlJobProperties.getExecutor().getLogRetentionDays()); / / reserved customizer configuration customizers. OrderedStream (). The forEach (customizer - > customizer. Customize (xxlJobExecutor)); return xxlJobExecutor; }}Copy the code

Refer to the official Starter to reserve a developer extension configuration

/** * Reserve a customized interface */ @functionalInterface public interface XxlJobExecutorCustomizer {void customize(final XxlJobExecutor xxlJobExecutor); }Copy the code

Here are a few notes to explain:

  • ConfigurationProperties — Reads the configuration Settings in the YML file to the class properties annotated by this annotation
  • @ EnableConfigurationProperties — let Spring scan be @ ConfigurationProperties annotation class
  • ConditionalOnClass – this configuration class is valid only if the IJobHandler class exists
  • ConditionalOnMissingBean – this will be injected only if a Bean of type XxlJobExecutor does not already exist in Spring. Our starter will not be injected into the Spring container

Enabling SpringBoot to scan our starter injection Bean

In the spring.factories file, write the following configuration, which is the official SpringBoot convention

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.syc.xxljob.autoconfigure.XxlJobAutoConfiguration
Copy the code

Final project structure

Use the stater that we make

Use Maven to install our previous project into the local repository, and then introduce the dependency coordinates in another SpringBoot project

<dependency> <groupId>com.syc</groupId> <artifactId>xxl-job-springboot-starter</artifactId> < version > 0.0.1 - the SNAPSHOT < / version > < / dependency >Copy the code

Setting the related attribute values in the YML file will automatically provide the previous comment prompt. This prompt function is provided by the previous spring-boot-configuration-processor dependency

After the configuration is written, start the SpringBoot project, which will automatically inject XxlJobSpringExecutor from our starter into Spring.

Difference from RedisTemplate

Note that for xxL-jobs we automatically configure the injection of XxlJobSpringExecutor objects into Spring. It is not used as RedisTemplate or RabbitTemplatge because they are not used the same way. XxlJobSpringExecutor must be injected into the Spring container implicitly for @xxlJob. RedisTemplate provides a tool template class directly.

The source address

Github full source address xxl-job-springboot-starter

conclusion

Please like and follow this article if it’s been helpful to you. Your support is my motivation to continue to create!