SpringBoot使用CommandLineRunner接口完成資源初始化方式
1 簡介
1.1 應(yīng)用場景
在應(yīng)用服務(wù)啟動時,需要在所有Bean生成之后,加載一些數(shù)據(jù)和執(zhí)行一些應(yīng)用的初始化。
例如:刪除臨時文件,清楚緩存信息,讀取配置文件,數(shù)據(jù)庫連接,這些工作類似開機自啟動的概念,CommandLineRunner、ApplicationRunner 接口是在容器啟動成功后的最后一步回調(diào)(類似開機自啟動)。
1.2 CommandLineRunner接口
CommandLineRunner源碼實現(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)一個bean包含在SpringApplication內(nèi),該bean就應(yīng)當(dāng)執(zhí)行??梢栽谙嗤膽?yīng)用上下文定義多個這樣的bean。多個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; }
在對該接口的注釋中,可以看到兩個接口的應(yīng)用場景,甚至注釋都是完全一樣的。
唯一的區(qū)別是接口中的函數(shù)run的參數(shù),一個是與main函數(shù)同樣的(String[] args),而另外一個是ApplicationArgumens類型。
在一般情況下,開發(fā)時是不需要添加命令行參數(shù)的,因此兩個接口的區(qū)別對于這樣的場景也就完全一樣了。
但如果真的需要類型–foo=bar的option arguments,為了方便起見,可以使用ApplicationRunner來讀取類似的參數(shù)。
1.4 @Order注解
Order注解的源碼實現(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接口的作用是完全一致的,唯一不同的則是接口中待實現(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}. * 提供用來運行一個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實現(xiàn)的的核心,簡單看下該類實現(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類的實現(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 背景介紹
在項目運行的過程中,項目會事先訂閱一些關(guān)注的事件,大的方向分為門禁事件和監(jiān)控點事件,其中上報上來的門禁事件的基本信息為門禁點信息,而不是門禁設(shè)備的唯一標(biāo)識,為了解析的方便,需要把上報上來的門禁點事件和門禁設(shè)備唯一標(biāo)識、IP信息關(guān)聯(lián)起來,這表示,在項目啟動之后,當(dāng)所有bean都完成了加載,需要在項目開始提供其他REST服務(wù)之前,把門禁點到門禁設(shè)備唯一標(biāo)識、IP等信息關(guān)聯(lián)起來。
而這個關(guān)聯(lián)的獲取,需要類似開機自啟動的功能一樣,需要項目一啟動,就獲取這個關(guān)聯(lián)。
其中,獲取所有門禁設(shè)備的基本信息可以通過AcsDeviceController獲取,而所有門禁點信息的獲取通過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": "門禁一體機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": "門禁一體機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。
而獲取所有的門禁點接口可以獲取如下的內(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": "門禁一體機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": "門禁一體機1_門1", "doorIndexCode": "139dcaecc5ba4b9fb97889c2f2234e79", "acsDevIndexCode": "71a9ea67d58a43db8c5dadfe2197a4db", "channelType": "door", "updateTime": "2019-04-11T20:12:16.012+08:00", "doorNo": "1" } ] } }
可以看到獲取到的門禁點信息,會同時包含門禁設(shè)備的唯一標(biāo)識字段,因此可以根據(jù)該字段門禁點唯一標(biāo)識獲得門禁點到門禁設(shè)備基本信息的映射。
當(dāng)系統(tǒng)訂閱了相關(guān)的事件之后,可以收到的事件報文格式如下:
{ "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)于門禁事件上報報文格式參見一卡通應(yīng)用服務(wù)
2.3 實現(xiàn)過程
在建立映射的過程中,只需要關(guān)注門禁設(shè)備的唯一標(biāo)識,ip,端口,門禁設(shè)備名稱。門禁點則只需要關(guān)注門禁點唯一標(biāo)識。
2.4 源碼實現(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.*; /** * 獲取門禁點相關(guā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
為了編程的方便,在代碼中為門禁點和門禁設(shè)備建模為類型,方便獲取某個特別屬性。比如說設(shè)備唯一標(biāo)識,門禁點唯一標(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: 新增,為門禁點到門禁設(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
門禁點,在為了建立映射的過程中,只需要關(guān)注門禁點唯一標(biāo)識和門禁設(shè)備唯一標(biāo)識,這樣在建立映射關(guān)系時,可以根據(jù)門禁設(shè)備唯一標(biāo)識獲取該設(shè)備的基本信息。
package com.example.platform.domain.dto; import com.alibaba.fastjson.JSONObject; /** * 門禁點建模為類 * * @Owner: SongQuanHeng * @Time: 2019/3/28-17:03 * @Version: * @Change: 把門禁點建模為類 */ public class AcsDoor { /** * 門禁點唯一標(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é)門禁點映射成門禁設(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ù)器里獲取所有門禁點信息,所有門禁設(shè)備信息,進而構(gòu)建DoorMappingAvsDevice的運行時實例,即從門禁點唯一標(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; //獲取所有門禁點 @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)建對象和維護對象之間的關(guān)系,而不是通過對象本身負(fù)責(zé)自己的創(chuàng)建和解決自己的依賴。依賴注入的主要目的是為了解耦。體現(xiàn)了一種”組合”的理念。
Spring IoC容器(Application Context)負(fù)責(zé)創(chuàng)建Bean,并通過容器將功能類Bean注入到你需要的Bean中,Spring提供使用XML,注解,Java配置、groovy配置實現(xiàn)Bean的創(chuàng)建和注入。
通過上述InitializeResource可以看到,我們可以為一個Bean類注入Controller實例,并且調(diào)用Controller的REST接口以獲取數(shù)據(jù),然后把這些獲取的數(shù)據(jù)放入了DoorMappingAcsDevice的靜態(tài)變量中。
3 總結(jié)
文檔主要介紹了CommandLineRunner接口的應(yīng)用場景、并根據(jù)一個實例演示了使用CommandLineRunner來進行項目啟動之后的資源初始化,并通過在@Component注入@Controller領(lǐng)會Spring依賴注入的基本思想。
希望能夠通過此文檔幫助使用Spring Boot開發(fā)的程序員可以靈活的使用CommandLineRunner來完成項目自啟動,資源初始化類似的工作需求。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java使用阿里云接口進行身份證實名認(rèn)證的示例實現(xiàn)
這篇文章主要介紹了使用阿里云接口進行身份證實名認(rèn)證的示例實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Netty核心功能之?dāng)?shù)據(jù)容器ByteBuf詳解
這篇文章主要為大家介紹了Netty核心功能之?dāng)?shù)據(jù)容器ByteBuf詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10MyBatis結(jié)果映射(ResultMap)的使用
在MyBatis中,結(jié)果映射是實現(xiàn)數(shù)據(jù)庫結(jié)果集到Java對象映射的核心,它不僅支持簡單的字段映射,還能處理字段名不一致、嵌套對象和集合映射等復(fù)雜場景,通過ResultMap,開發(fā)者可以靈活定義映射關(guān)系,以適應(yīng)各種需求,感興趣的可以了解一下2024-09-09SpringBoot使用JTA實現(xiàn)對多數(shù)據(jù)源的事務(wù)管理
了解事務(wù)的都知道,在我們?nèi)粘i_發(fā)中單單靠事務(wù)管理就可以解決絕大多數(shù)問題了,但是為啥還要提出JTA這個玩意呢,到底JTA是什么呢?他又是具體來解決啥問題的呢?本文小編就給大家介紹一下如何在Spring Boot中使用JTA實現(xiàn)對多數(shù)據(jù)源的事務(wù)管理2023-11-11Springboot?內(nèi)部服務(wù)調(diào)用方式
這篇文章主要介紹了Springboot?內(nèi)部服務(wù)調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03Springboot Redis設(shè)置key前綴的方法步驟
這篇文章主要介紹了Springboot Redis設(shè)置key前綴的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Pattern.compile函數(shù)提取字符串中指定的字符(推薦)
這篇文章主要介紹了Pattern.compile函數(shù)提取字符串中指定的字符,使用的是Java中的Pattern.compile函數(shù)來實現(xiàn)對指定字符串的截取,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12Spring線程池ThreadPoolTaskExecutor的用法及說明
這篇文章主要介紹了Spring線程池ThreadPoolTaskExecutor的用法及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07