SpringBoot使用CommandLineRunner接口完成資源初始化方式
1 簡介
1.1 應(yīng)用場景
在應(yīng)用服務(wù)啟動時(shí),需要在所有Bean生成之后,加載一些數(shù)據(jù)和執(zhí)行一些應(yīng)用的初始化。
例如:刪除臨時(shí)文件,清楚緩存信息,讀取配置文件,數(shù)據(jù)庫連接,這些工作類似開機(jī)自啟動的概念,CommandLineRunner、ApplicationRunner 接口是在容器啟動成功后的最后一步回調(diào)(類似開機(jī)自啟動)。
1.2 CommandLineRunner接口
CommandLineRunner源碼實(shí)現(xiàn)如下:
package org.springframework.boot;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* Interface used to indicate that a bean should <em>run</em> when it is contained within
* a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined
* within the same application context and can be ordered using the {@link Ordered}
* interface or {@link Order @Order} annotation.
* <p>
* If you need access to {@link ApplicationArguments} instead of the raw String array
* consider using {@link ApplicationRunner}.
*
* @author Dave Syer
* @see ApplicationRunner
*/
@FunctionalInterface
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
對該接口的注釋可以看到如下的含義:
該接口用來指明:當(dāng)一個(gè)bean包含在SpringApplication內(nèi),該bean就應(yīng)當(dāng)執(zhí)行??梢栽谙嗤膽?yīng)用上下文定義多個(gè)這樣的bean。多個(gè)bean的先后執(zhí)行順序使用@Order注解確定。
1.3 ApplicationRunner接口
ApplicationRunner接口源碼定義如下:
package org.springframework.boot;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* Interface used to indicate that a bean should <em>run</em> when it is contained within
* a {@link SpringApplication}. Multiple {@link ApplicationRunner} beans can be defined
* within the same application context and can be ordered using the {@link Ordered}
* interface or {@link Order @Order} annotation.
*
* @author Phillip Webb
* @since 1.3.0
* @see CommandLineRunner
*/
@FunctionalInterface
public interface ApplicationRunner {
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}
在對該接口的注釋中,可以看到兩個(gè)接口的應(yīng)用場景,甚至注釋都是完全一樣的。
唯一的區(qū)別是接口中的函數(shù)run的參數(shù),一個(gè)是與main函數(shù)同樣的(String[] args),而另外一個(gè)是ApplicationArgumens類型。
在一般情況下,開發(fā)時(shí)是不需要添加命令行參數(shù)的,因此兩個(gè)接口的區(qū)別對于這樣的場景也就完全一樣了。
但如果真的需要類型–foo=bar的option arguments,為了方便起見,可以使用ApplicationRunner來讀取類似的參數(shù)。
1.4 @Order注解
Order注解的源碼實(shí)現(xiàn)如下:
package org.springframework.core.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.Ordered;
/**
* {@code @Order} defines the sort order for an annotated component.
*
* <p>The {@link #value} is optional and represents an order value as defined in the
* {@link Ordered} interface. Lower values have higher priority. The default value is
* {@code Ordered.LOWEST_PRECEDENCE}, indicating lowest priority (losing to any other
* specified order value).
*
* <p><b>NOTE:</b> Since Spring 4.0, annotation-based ordering is supported for many
* kinds of components in Spring, even for collection injection where the order values
* of the target components are taken into account (either from their target class or
* from their {@code @Bean} method). While such order values may influence priorities
* at injection points, please be aware that they do not influence singleton startup
* order which is an orthogonal concern determined by dependency relationships and
* {@code @DependsOn} declarations (influencing a runtime-determined dependency graph).
*
* <p>Since Spring 4.1, the standard {@link javax.annotation.Priority} annotation
* can be used as a drop-in replacement for this annotation in ordering scenarios.
* Note that {@code @Priority} may have additional semantics when a single element
* has to be picked (see {@link AnnotationAwareOrderComparator#getPriority}).
*
* <p>Alternatively, order values may also be determined on a per-instance basis
* through the {@link Ordered} interface, allowing for configuration-determined
* instance values instead of hard-coded values attached to a particular class.
*
* <p>Consult the javadoc for {@link org.springframework.core.OrderComparator
* OrderComparator} for details on the sort semantics for non-ordered objects.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.core.Ordered
* @see AnnotationAwareOrderComparator
* @see OrderUtils
* @see javax.annotation.Priority
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
/**
* The order value.
* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
* @see Ordered#getOrder()
*/
int value() default Ordered.LOWEST_PRECEDENCE;
}
可以看到該注解的作用主要是用來定義被注解的組件的排序順序。
注意:Lower values have higher priority。值越小越先執(zhí)行。
1.5 CommandLineRunner和ApplicationRunner區(qū)別
從上面的分析可以看出,CommandLineRunner和ApplicationRunner接口的作用是完全一致的,唯一不同的則是接口中待實(shí)現(xiàn)的run方法,其中CommandLineRunner的run方法參數(shù)類型與main一樣是原生的String[] 類型,而ApplicationRunner的run方法參數(shù)類型為ApplicationArguments類型。
ApplicationArguments類型源碼定義如下:
package org.springframework.boot;
import java.util.List;
import java.util.Set;
/**
* Provides access to the arguments that were used to run a {@link SpringApplication}.
* 提供用來運(yùn)行一個(gè)SpringApplication的參數(shù)。
* @author Phillip Webb
* @since 1.3.0
*/
public interface ApplicationArguments {
/**
* Return the raw unprocessed arguments that were passed to the application.
* @return the arguments
*/
String[] getSourceArgs();
/**
* Return the names of all option arguments. For example, if the arguments were
* "--foo=bar --debug" would return the values {@code ["foo", "debug"]}.
* @return the option names or an empty set
*/
Set<String> getOptionNames();
/**
* Return whether the set of option arguments parsed from the arguments contains an
* option with the given name.
* @param name the name to check
* @return {@code true} if the arguments contain an option with the given name
*/
boolean containsOption(String name);
/**
* Return the collection of values associated with the arguments option having the
* given name.
* <ul>
* <li>if the option is present and has no argument (e.g.: "--foo"), return an empty
* collection ({@code []})</li>
* <li>if the option is present and has a single value (e.g. "--foo=bar"), return a
* collection having one element ({@code ["bar"]})</li>
* <li>if the option is present and has multiple values (e.g. "--foo=bar --foo=baz"),
* return a collection having elements for each value ({@code ["bar", "baz"]})</li>
* <li>if the option is not present, return {@code null}</li>
* </ul>
* @param name the name of the option
* @return a list of option values for the given name
*/
List<String> getOptionValues(String name);
/**
* Return the collection of non-option arguments parsed.
* @return the non-option arguments or an empty list
*/
List<String> getNonOptionArgs();
}

ApplicaitonArguments類型之間的關(guān)系如下:

1.5.1 option-argument和non-option arguments

參見命令解釋中option和non-option參數(shù)
1.5.2 SimpleCommandLineArgsParser
由于SimpleCommandLineArgsParser的功能是ApplicationArguments實(shí)現(xiàn)的的核心,簡單看下該類實(shí)現(xiàn)的源碼:
package org.springframework.core.env;
/**
* Parses a {@code String[]} of command line arguments in order to populate a
* {@link CommandLineArgs} object.
*
* <h3>Working with option arguments</h3>
* Option arguments must adhere to the exact syntax:
* <pre class="code">--optName[=optValue]</pre>
* That is, options must be prefixed with "{@code --}", and may or may not specify a value.
* If a value is specified, the name and value must be separated <em>without spaces</em>
* by an equals sign ("=").
*
* <h4>Valid examples of option arguments</h4>
* <pre class="code">
* --foo
* --foo=bar
* --foo="bar then baz"
* --foo=bar,baz,biz</pre>
*
* <h4>Invalid examples of option arguments</h4>
* <pre class="code">
* -foo
* --foo bar
* --foo = bar
* --foo=bar --foo=baz --foo=biz</pre>
*
* <h3>Working with non-option arguments</h3>
* Any and all arguments specified at the command line without the "{@code --}" option
* prefix will be considered as "non-option arguments" and made available through the
* {@link CommandLineArgs#getNonOptionArgs()} method.
*
* @author Chris Beams
* @since 3.1
*/
class SimpleCommandLineArgsParser {
/**
* Parse the given {@code String} array based on the rules described {@linkplain
* SimpleCommandLineArgsParser above}, returning a fully-populated
* {@link CommandLineArgs} object.
* @param args command line arguments, typically from a {@code main()} method
*/
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionName;
String optionValue = null;
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf('='));
optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
}
else {
optionName = optionText;
}
if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
}
在程序執(zhí)行過程中,SimpleCommandLineArgsParser會把程序的參數(shù)分解成option arguments和nonoption arguments,然后放入CommandLineArgs,

CommandLineArgs類的實(shí)現(xiàn)如下:
package org.springframework.core.env;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.lang.Nullable;
/**
* A simple representation of command line arguments, broken into "option arguments" and
* "non-option arguments".
*
* @author Chris Beams
* @since 3.1
* @see SimpleCommandLineArgsParser
*/
class CommandLineArgs {
private final Map<String, List<String>> optionArgs = new HashMap<>();
private final List<String> nonOptionArgs = new ArrayList<>();
/**
* Add an option argument for the given option name and add the given value to the
* list of values associated with this option (of which there may be zero or more).
* The given value may be {@code null}, indicating that the option was specified
* without an associated value (e.g. "--foo" vs. "--foo=bar").
*/
public void addOptionArg(String optionName, @Nullable String optionValue) {
if (!this.optionArgs.containsKey(optionName)) {
this.optionArgs.put(optionName, new ArrayList<>());
}
if (optionValue != null) {
this.optionArgs.get(optionName).add(optionValue);
}
}
/**
* Return the set of all option arguments present on the command line.
*/
public Set<String> getOptionNames() {
return Collections.unmodifiableSet(this.optionArgs.keySet());
}
/**
* Return whether the option with the given name was present on the command line.
*/
public boolean containsOption(String optionName) {
return this.optionArgs.containsKey(optionName);
}
/**
* Return the list of values associated with the given option. {@code null} signifies
* that the option was not present; empty list signifies that no values were associated
* with this option.
*/
@Nullable
public List<String> getOptionValues(String optionName) {
return this.optionArgs.get(optionName);
}
/**
* Add the given value to the list of non-option arguments.
*/
public void addNonOptionArg(String value) {
this.nonOptionArgs.add(value);
}
/**
* Return the list of non-option arguments specified on the command line.
*/
public List<String> getNonOptionArgs() {
return Collections.unmodifiableList(this.nonOptionArgs);
}
}
2 CommandLineRunner完成資源初始化
2.1 背景介紹
在項(xiàng)目運(yùn)行的過程中,項(xiàng)目會事先訂閱一些關(guān)注的事件,大的方向分為門禁事件和監(jiān)控點(diǎn)事件,其中上報(bào)上來的門禁事件的基本信息為門禁點(diǎn)信息,而不是門禁設(shè)備的唯一標(biāo)識,為了解析的方便,需要把上報(bào)上來的門禁點(diǎn)事件和門禁設(shè)備唯一標(biāo)識、IP信息關(guān)聯(lián)起來,這表示,在項(xiàng)目啟動之后,當(dāng)所有bean都完成了加載,需要在項(xiàng)目開始提供其他REST服務(wù)之前,把門禁點(diǎn)到門禁設(shè)備唯一標(biāo)識、IP等信息關(guān)聯(lián)起來。
而這個(gè)關(guān)聯(lián)的獲取,需要類似開機(jī)自啟動的功能一樣,需要項(xiàng)目一啟動,就獲取這個(gè)關(guān)聯(lián)。
其中,獲取所有門禁設(shè)備的基本信息可以通過AcsDeviceController獲取,而所有門禁點(diǎn)信息的獲取通過AcsDoorController獲取。
2.2 數(shù)據(jù)類型關(guān)系
調(diào)用獲取所有門禁設(shè)備信息,可以獲得形似如下的數(shù)據(jù):
{
"msg": "success",
"code": "0",
"data": {
"list": [
{
"regionIndexCode": "a6745925-0415-4853-9815-bb9707e2ad8b",
"acsDevTypeCode": "201933568",
"createTime": "2019-04-11T20:10:16.184+08:00",
"acsDevTypeDesc": "DS-K1T604MF",
"acsDevName": "門禁一體機(jī)1",
"acsDevIndexCode": "71a9ea67d58a43db8c5dadfe2197a4db",
"updateTime": "2019-04-11T20:18:01.683+08:00",
"acsDevIp": "192.168.1.103",
"acsDevPort": "8000",
"treatyType": "hiksdk_net"
},
{
"regionIndexCode": "a6745925-0415-4853-9815-bb9707e2ad8b",
"acsDevTypeCode": "201933568",
"createTime": "2019-04-11T20:12:06.137+08:00",
"acsDevTypeDesc": "DS-K1T604MF",
"acsDevName": "門禁一體機(jī)3",
"acsDevIndexCode": "13891ae9f6454782a208504e72ba2ad8",
"updateTime": "2019-04-11T20:12:07.876+08:00",
"acsDevIp": "192.168.1.105",
"acsDevPort": "8000",
"treatyType": "hiksdk_net"
}
]
}
}
可以看到所有的門禁設(shè)備均包含設(shè)備的唯一標(biāo)識acsDevIndexCode、acsDevName門禁設(shè)備名稱、門禁設(shè)備IP地址acsDevIp,門禁設(shè)備端口acsDevPort。
而獲取所有的門禁點(diǎn)接口可以獲取如下的內(nèi)容:
{
"msg": "success",
"code": "0",
"data": {
"list": [
{
"regionIndexCode": "a6745925-0415-4853-9815-bb9707e2ad8b",
"channelNo": "1",
"createTime": "2019-04-11T20:12:06.166+08:00",
"doorName": "門禁一體機(jī)3_門1",
"doorIndexCode": "a87022481f8242b29a4bf35a57edc004",
"acsDevIndexCode": "13891ae9f6454782a208504e72ba2ad8",
"channelType": "door",
"updateTime": "2019-04-11T20:12:16.012+08:00",
"doorNo": "1"
},
{
"regionIndexCode": "a6745925-0415-4853-9815-bb9707e2ad8b",
"channelNo": "1",
"createTime": "2019-04-11T20:10:17.826+08:00",
"doorName": "門禁一體機(jī)1_門1",
"doorIndexCode": "139dcaecc5ba4b9fb97889c2f2234e79",
"acsDevIndexCode": "71a9ea67d58a43db8c5dadfe2197a4db",
"channelType": "door",
"updateTime": "2019-04-11T20:12:16.012+08:00",
"doorNo": "1"
}
]
}
}
可以看到獲取到的門禁點(diǎn)信息,會同時(shí)包含門禁設(shè)備的唯一標(biāo)識字段,因此可以根據(jù)該字段門禁點(diǎn)唯一標(biāo)識獲得門禁點(diǎn)到門禁設(shè)備基本信息的映射。
當(dāng)系統(tǒng)訂閱了相關(guān)的事件之后,可以收到的事件報(bào)文格式如下:
{
"method": "OnEventNotify",
"params": {
"ability": "event_acs",
"events": [{
"data": {
"ExtAccessChannel": 0,
"ExtEventAlarmInID": 0,
"ExtEventAlarmOutID": 0,
"ExtEventCardNo": "1874193998",
"ExtEventCaseID": 0,
"ExtEventCode": 197634,
"ExtEventCustomerNumInfo": {
"AccessChannel": 0,
"EntryTimes": 0,
"ExitTimes": 0,
"TotalTimes": 0
},
"ExtEventDoorID": 1,
"ExtEventIDCardPictureURL": "",
"ExtEventIdentityCardInfo": {
"Address": "",
"Birth": "",
"EndDate": "",
"IdNum": "",
"IssuingAuthority": "",
"Name": "",
"Nation": 0,
"Sex": 0,
"StartDate": "",
"TermOfValidity": 0
},
"ExtEventInOut": 1,
"ExtEventLocalControllerID": 0,
"ExtEventMainDevID": 1,
"ExtEventPersonNo": "0",
"ExtEventPictureURL": "/pic?=d2=i689z260ds986-125mfep=t9i2i*d1=*ipd1=*isd8=*dbec775bf-84fbc12-484868-82i167*e172ed5",
"ExtEventReaderID": 1,
"ExtEventReaderKind": 0,
"ExtEventReportChannel": 0,
"ExtEventRoleID": 0,
"ExtEventSubDevID": 0,
"ExtEventSwipNum": 0,
"ExtEventType": 0,
"ExtEventVerifyID": 0,
"ExtEventWhiteListNo": 0,
"ExtReceiveTime": "1548828922319128",
"Seq": 0,
"svrIndexCode": "b1cded2b-2fbc-4255-aa9d-45162bfa23dd"
},
"eventId": "6B15F0C9947949D5BF271327C66BD658",
"eventType": 197634,
"eventTypeName": "acs.acs.eventType.wrongNoSuchCard",
"happenTime": "2019-01-30T12:44:59.000+08:00",
"srcIndex": "92ca80c786b843058e764f7fda863ae1",
"srcName": "10.13.65.181_門1",
"srcParentIndex": "de948e1856f74253b65848f4bef3fb03",
"srcType": "door",
"status": 0,
"timeout": 0
}],
"sendTime": "2019-01-30T14:15:22.000+08:00"
}
}
關(guān)于門禁事件上報(bào)報(bào)文格式參見一卡通應(yīng)用服務(wù)
2.3 實(shí)現(xiàn)過程

在建立映射的過程中,只需要關(guān)注門禁設(shè)備的唯一標(biāo)識,ip,端口,門禁設(shè)備名稱。門禁點(diǎn)則只需要關(guān)注門禁點(diǎn)唯一標(biāo)識。
2.4 源碼實(shí)現(xiàn)
2.4.1 Controller
2.4.1.1 AcsDeviceController
AcsDeviceController用來獲取門禁設(shè)備的唯一標(biāo)識。
package com.example.platform.controller;
import com.example.platform.constants.Artemis;
import com.example.platform.utils.HttpClientSSLUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* 獲取門禁設(shè)備信息的控制器
*
* @Owner:
* @Time: 2019/3/1-15:45
*/
@RestController
@RequestMapping("/api/resource")
public class AcsDeviceController {
private static final Logger logger = LoggerFactory.getLogger(AcsDeviceController.class);
@Autowired
private HttpClientSSLUtil httpClientSSLUtil;
private static Map<String, String> acsDeviceAccessingUrl = new HashMap<>();
static {
acsDeviceAccessingUrl.put("allAcsDevice", Artemis.ARTEMIS_PATH+"/api/resource/v1/acsDevice/acsDeviceList");
acsDeviceAccessingUrl.put("acsDeviceList", Artemis.ARTEMIS_PATH+"/api/resource/v1/acsDevice/advance/acsDeviceList");
acsDeviceAccessingUrl.put("aAcsDeviceInfo", Artemis.ARTEMIS_PATH+"/api/resource/v1/acsDevice/indexCode/acsDeviceInfo");
}
public String getAllAcsDeviceUrl() {
return acsDeviceAccessingUrl.get("allAcsDevice");
}
/**
* @description: 獲取門禁設(shè)備列表
* @url:
* @author: Song Quanheng
* @date: 2019/3/1-16:30
* @return:
*/
@GetMapping("/acsDevice/allAcsDevices")
@ResponseBody
public String getAllAcsDevices() {
logger.info("Enter getAllAcsDevices");
return httpClientSSLUtil.extractFullResourceList(getAllAcsDeviceUrl());
}
}
門禁設(shè)備信息參見門禁設(shè)備信息
2.4.1.2 AcsDoorController
package com.example.platform.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.platform.constants.Artemis;
import com.example.platform.service.CommonReturn;
import com.example.platform.utils.HttpClientSSLUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* 獲取門禁點(diǎn)相關(guān)的信息,可支持按條件查詢,全量查詢,查詢單個(gè)門禁點(diǎn)的信息
*
* @Owner:
* @Time: 2019/3/1-16:43
*/
@RestController
@RequestMapping("/api")
public class AcsDoorController {
private static final Logger LOGGER = LoggerFactory.getLogger(AcsDoorController.class);
public static final int ALWAYS_OPEN = 0;
public static final int CLOSE = 1;
public static final int OPEN = 2;
public static final int ALWAYS_CLOSE = 3;
public static final int MAX_DOOR_INDEX_NUM = 10;
@Autowired
private HttpClientSSLUtil httpClientSSLUtil;
private static Map<String, String> acsDoorUrl = new HashMap<>();
static {
acsDoorUrl.put("allDoor", Artemis.ARTEMIS_PATH+"/api/resource/v1/acsDoor/acsDoorList");
acsDoorUrl.put("doorList", Artemis.ARTEMIS_PATH+"/api/resource/v1/acsDoor/advance/acsDoorList");
acsDoorUrl.put("aDoorInfo", Artemis.ARTEMIS_PATH+"/api/resource/v1/acsDoor/indexCode/acsDoorInfo");
acsDoorUrl.put("doorControl", Artemis.ARTEMIS_PATH+"/api/acs/v1/door/doControl");
}
public String getAllDoorUrl() {
return acsDoorUrl.get("allDoor");
}
public String getDoorControlUrl() {
return acsDoorUrl.get("doorControl");
}
@GetMapping("/resource/acsDoor/allAcsDoors")
public String getAllDoors() {
LOGGER.info("Enter getAllDoors");
return httpClientSSLUtil.extractFullResourceList(getAllDoorUrl());
}
@PostMapping(value = "/acs/door/doControl", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String controlDoor(@RequestBody JSONObject controlInfo) {
if (hasValidControlInfo(controlInfo)) {
LOGGER.debug("Enter controlDoor");
return CommonReturn.httpReturnFailure("輸入的反控信息不正確");
}
httpClientSSLUtil.setHttpsUrl(getDoorControlUrl());
return httpClientSSLUtil.doPostString(controlInfo.toJSONString());
}
private boolean hasValidControlInfo(JSONObject controlInfo) {
if (!controlInfo.containsKey("doorIndexCodes") || !controlInfo.containsKey("controlType")) {
return false;
}
JSONArray doorIndexCodes = controlInfo.getJSONArray("doorIndexCodes");
if (doorIndexCodes.size()>MAX_DOOR_INDEX_NUM) {
LOGGER.info("doorIndexCodes.size()>MAX_DOOR_INDEX_NUM");
return false;
}
int controlType = controlInfo.getIntValue("controlType");
Set<Integer> doorControlType = new HashSet<>();
Collections.addAll(doorControlType, ALWAYS_OPEN, CLOSE, OPEN, ALWAYS_CLOSE);
if (doorControlType.contains(controlType)) {
LOGGER.debug("unsupported controlType: "+controlType);
return false;
}
return true;
}
}
2.4.2 Model
為了編程的方便,在代碼中為門禁點(diǎn)和門禁設(shè)備建模為類型,方便獲取某個(gè)特別屬性。比如說設(shè)備唯一標(biāo)識,門禁點(diǎn)唯一標(biāo)識。
2.4.2.1 AcsDevice
package com.example.platform.domain.dto;
import com.alibaba.fastjson.JSONObject;
/**
* 門禁設(shè)備屬性說明
*
* @Owner: SongQuanHeng
* @Time: 2019/3/28-15:56
* @Version:
* @Change: 新增,為門禁點(diǎn)到門禁設(shè)備的映射新增類型。
*/
public class AcsDevice {
/**
* 門禁設(shè)備唯一標(biāo)識
*/
private String acsDevIndexCode;
private String acsDevName;
private String acsDevTypeDesc;
private String acsDevTypeCode;
private String acsDevTypeName;
/**
* 門禁設(shè)備Ip和端口
*/
private String acsDevIp;
private String acsDevPort;
private String acsDevCode;
private String regionIndexCode;
private String treatyType;
private String createTime;
private String updateTme;
public AcsDevice(JSONObject devInfo) {
this.acsDevIndexCode = devInfo.getString("acsDevIndexCode");
this.acsDevIp = devInfo.getString("acsDevIp");
this.acsDevPort = devInfo.getString("acsDevPort");
}
public String getAcsDevIndexCode() {
return acsDevIndexCode;
}
public String getAcsDevIp() {
return acsDevIp;
}
public String getAcsDevPort() {
return acsDevPort;
}
}
2.4.2.2 AcsDoor
門禁點(diǎn),在為了建立映射的過程中,只需要關(guān)注門禁點(diǎn)唯一標(biāo)識和門禁設(shè)備唯一標(biāo)識,這樣在建立映射關(guān)系時(shí),可以根據(jù)門禁設(shè)備唯一標(biāo)識獲取該設(shè)備的基本信息。
package com.example.platform.domain.dto;
import com.alibaba.fastjson.JSONObject;
/**
* 門禁點(diǎn)建模為類
*
* @Owner: SongQuanHeng
* @Time: 2019/3/28-17:03
* @Version:
* @Change: 把門禁點(diǎn)建模為類
*/
public class AcsDoor {
/**
* 門禁點(diǎn)唯一標(biāo)識
*/
private String doorIndexCode;
/**
* 所屬門禁設(shè)備唯一標(biāo)識
*/
private String acsDevIndexCode;
public AcsDoor(JSONObject doorInfo) {
this.doorIndexCode = doorInfo.getString("doorIndexCode");
this.acsDevIndexCode = doorInfo.getString("acsDevIndexCode");
}
public String getDoorIndexCode() {
return doorIndexCode;
}
public void setDoorIndexCode(String doorIndexCode) {
this.doorIndexCode = doorIndexCode;
}
public String getAcsDevIndexCode() {
return acsDevIndexCode;
}
public void setAcsDevIndexCode(String acsDevIndexCode) {
this.acsDevIndexCode = acsDevIndexCode;
}
}
2.4.2.3 DoorMappingAcsDevice
DoorMappingAcsDevice類對應(yīng)的bean的靜態(tài)成員doorMapDevs負(fù)責(zé)保存映射關(guān)系。
package com.example.platform.constants;
import com.example.platform.domain.dto.AcsDevice;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 該類負(fù)責(zé)門禁點(diǎn)映射成門禁設(shè)備
*
* @Owner: SongQuanHeng
* @Time: 2019/3/28-16:18
* @Version:
* @Change:
*/
@Component
public class DoorMappingAcsDevice {
private static Map<String, AcsDevice> doorMapDevs = new HashMap<>();
public AcsDevice getAcsDevice(String doorIndexCode) {
return doorMapDevs.get(doorIndexCode);
}
public void addAcsDevice(String doorIndexCode, AcsDevice device) {
doorMapDevs.put(doorIndexCode, device);
}
public static Map<String, AcsDevice> getDoorMapDevs() {
return Collections.unmodifiableMap(doorMapDevs);
}
}
2.4.3 CommandLiner
該接口負(fù)責(zé)調(diào)用所有的Controller從服務(wù)器里獲取所有門禁點(diǎn)信息,所有門禁設(shè)備信息,進(jìn)而構(gòu)建DoorMappingAvsDevice的運(yùn)行時(shí)實(shí)例,即從門禁點(diǎn)唯一標(biāo)識到門禁設(shè)備信息的映射。
package com.example.platform.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.platform.constants.DoorMappingAcsDevice;
import com.example.platform.controller.AcsDeviceController;
import com.example.platform.controller.AcsDoorController;
import com.example.platform.domain.dto.AcsDevice;
import com.example.platform.domain.dto.AcsDoor;
import com.example.platform.utils.ReturnResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 在所有Bean生成之后初始化資源
* 通過在把該InitializeResource初始化資源類使用@Component注解,令該類成為容器管理的bean。
* @Owner: SongQuanHeng
* @Time: 2019/3/28-16:38
* @Version:
* @Change:
*/
@Component
public class InitializeResource implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//獲取所有門禁設(shè)備
@Autowired
private AcsDeviceController acsDeviceController;
//獲取所有門禁點(diǎn)
@Autowired
private AcsDoorController acsDoorController;
@Autowired
private DoorMappingAcsDevice doorMappingAcsDevice;
@Override
public void run(String... args) throws Exception {
logger.debug("Enter run");
Map<String, AcsDevice> devMap = new HashMap<>();
ReturnResult resDevs = new ReturnResult(acsDeviceController.getAllAcsDevices());
if (!resDevs.isRequestSuccessful()) {
logger.debug("acsDeviceController.getAllAcsDevices() fails");
throw new Exception("acsDeviceController.getAllAcsDevices() fails");
}
JSONArray allDevs = resDevs.getResourceList();
for (int i = 0; i < allDevs.size(); i++) {
AcsDevice device = new AcsDevice(allDevs.getJSONObject(i));
devMap.put(device.getAcsDevIndexCode(), device);
}
ReturnResult resDoors = new ReturnResult(acsDoorController.getAllDoors());
if (!resDevs.isRequestSuccessful()) {
logger.debug("acsDoorController.getAllDoors() fails");
throw new Exception("acsDoorController.getAllDoors() fails");
}
JSONArray allDoors = resDoors.getResourceList();
for (int i = 0; i < allDoors.size(); i++) {
JSONObject doorInfo = allDoors.getJSONObject(i);
AcsDoor door = new AcsDoor(allDoors.getJSONObject(i));
String doorAcsDevIndexCode = door.getAcsDevIndexCode();
if (!devMap.containsKey(doorAcsDevIndexCode)) {
logger.debug("Impossible situation");
} else {
AcsDevice device = devMap.get(doorAcsDevIndexCode);
doorMappingAcsDevice.addAcsDevice(door.getDoorIndexCode(), device);
}
}
System.out.println("The Runner start to initialize");
}
}
在此簡要的介紹依賴注入的概念。所謂依賴注入指的是容器負(fù)責(zé)創(chuàng)建對象和維護(hù)對象之間的關(guān)系,而不是通過對象本身負(fù)責(zé)自己的創(chuàng)建和解決自己的依賴。依賴注入的主要目的是為了解耦。體現(xiàn)了一種”組合”的理念。
Spring IoC容器(Application Context)負(fù)責(zé)創(chuàng)建Bean,并通過容器將功能類Bean注入到你需要的Bean中,Spring提供使用XML,注解,Java配置、groovy配置實(shí)現(xiàn)Bean的創(chuàng)建和注入。
通過上述InitializeResource可以看到,我們可以為一個(gè)Bean類注入Controller實(shí)例,并且調(diào)用Controller的REST接口以獲取數(shù)據(jù),然后把這些獲取的數(shù)據(jù)放入了DoorMappingAcsDevice的靜態(tài)變量中。
3 總結(jié)
文檔主要介紹了CommandLineRunner接口的應(yīng)用場景、并根據(jù)一個(gè)實(shí)例演示了使用CommandLineRunner來進(jìn)行項(xiàng)目啟動之后的資源初始化,并通過在@Component注入@Controller領(lǐng)會Spring依賴注入的基本思想。
希望能夠通過此文檔幫助使用Spring Boot開發(fā)的程序員可以靈活的使用CommandLineRunner來完成項(xiàng)目自啟動,資源初始化類似的工作需求。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java使用阿里云接口進(jìn)行身份證實(shí)名認(rèn)證的示例實(shí)現(xiàn)
這篇文章主要介紹了使用阿里云接口進(jìn)行身份證實(shí)名認(rèn)證的示例實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Netty核心功能之?dāng)?shù)據(jù)容器ByteBuf詳解
這篇文章主要為大家介紹了Netty核心功能之?dāng)?shù)據(jù)容器ByteBuf詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
MyBatis結(jié)果映射(ResultMap)的使用
在MyBatis中,結(jié)果映射是實(shí)現(xiàn)數(shù)據(jù)庫結(jié)果集到Java對象映射的核心,它不僅支持簡單的字段映射,還能處理字段名不一致、嵌套對象和集合映射等復(fù)雜場景,通過ResultMap,開發(fā)者可以靈活定義映射關(guān)系,以適應(yīng)各種需求,感興趣的可以了解一下2024-09-09
SpringBoot使用JTA實(shí)現(xiàn)對多數(shù)據(jù)源的事務(wù)管理
了解事務(wù)的都知道,在我們?nèi)粘i_發(fā)中單單靠事務(wù)管理就可以解決絕大多數(shù)問題了,但是為啥還要提出JTA這個(gè)玩意呢,到底JTA是什么呢?他又是具體來解決啥問題的呢?本文小編就給大家介紹一下如何在Spring Boot中使用JTA實(shí)現(xiàn)對多數(shù)據(jù)源的事務(wù)管理2023-11-11
Springboot?內(nèi)部服務(wù)調(diào)用方式
這篇文章主要介紹了Springboot?內(nèi)部服務(wù)調(diào)用方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
java實(shí)現(xiàn)簡單登錄界面的實(shí)戰(zhàn)過程
學(xué)習(xí)JAVA的過程當(dāng)中,登陸界面是多數(shù)人第一個(gè)實(shí)現(xiàn)的小程序,下面這篇文章主要給大家介紹了關(guān)于利用java實(shí)現(xiàn)簡單登錄界面的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
Springboot Redis設(shè)置key前綴的方法步驟
這篇文章主要介紹了Springboot Redis設(shè)置key前綴的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Pattern.compile函數(shù)提取字符串中指定的字符(推薦)
這篇文章主要介紹了Pattern.compile函數(shù)提取字符串中指定的字符,使用的是Java中的Pattern.compile函數(shù)來實(shí)現(xiàn)對指定字符串的截取,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12
Spring線程池ThreadPoolTaskExecutor的用法及說明
這篇文章主要介紹了Spring線程池ThreadPoolTaskExecutor的用法及說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07

