ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## 简介 通常我们启动工程都是使用`SpringApplication.run(Application.class, args);`来启动,但是JPower工程已经设置了很多自定义参数用来集成不同组件,故采用`SpringApplicationBuilder`,将其封装进一个核心类中进行拓展,以便于所有微服务调用启动之用。 ## 自定义启动器 * 如需要自行新增自定义参数在其核心启动类中的新增即可。 * 启动器核心代码如下 ~~~ /** * @ClassName JpowerApplication * @Description TODO 项目启动器 * @Author Ding * @Date 2020-08-02 17:04 * @Version 1.0 */ @Slf4j public class JpowerApplication { /** * @Author 郭丁志 * @Description //TODO 项目启动 * @Date 18:16 2020-08-02 * @Param appName 项目模块名称,必须与client表的code值相同 * @return org.springframework.context.ConfigurableApplicationContext **/ public static ConfigurableApplicationContext run(String appName, Class source, String... args) { SpringApplicationBuilder builder = createSpringApplicationBuilder(appName, source, args); return builder.run(args); } private static SpringApplicationBuilder createSpringApplicationBuilder(String appName, Class source, String[] args) { Assert.hasText(appName, "[appName]服务名不能为空"); SpringApplicationBuilder builder = new SpringApplicationBuilder(source); //读取环境变量配置 ConfigurableEnvironment environment = new StandardEnvironment(); MutablePropertySources propertySources = environment.getPropertySources(); propertySources.addFirst(new SimpleCommandLinePropertySource(args)); propertySources.addLast(new MapPropertySource("systemProperties", environment.getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", environment.getSystemEnvironment())); // 获取配置的环境变量 String[] activeProfiles = environment.getActiveProfiles(); // 判断环境:dev、test、prod List<String> profiles = Arrays.asList(activeProfiles); List<String> presetProfiles = new ArrayList(Arrays.asList("dev", "test", "prod")); presetProfiles.retainAll(profiles); List<String> activeProfileList = new ArrayList<>(presetProfiles); String profile; if (activeProfileList.isEmpty()) { // 默认dev开发 profile = AppConstant.DEV_CODE; activeProfileList.add(profile); builder.profiles(profile); } else if (activeProfileList.size() == 1) { profile = activeProfileList.get(0); } else { // 同时存在dev、test、prod环境抛出错误 throw new RuntimeException("同时存在环境变量:[" + StringUtils.arrayToCommaDelimitedString(activeProfiles) + "]"); } Properties props = System.getProperties(); props.setProperty("spring.application.name", appName); props.setProperty("spring.profiles.active", profile); props.setProperty("logging.config", "classpath:logback-spring.xml"); props.setProperty("jpower.is-local", String.valueOf(isLocalDev())); props.setProperty("spring.main.allow-bean-definition-overriding", "true"); //nacos配置 props.setProperty("spring.cloud.nacos.discovery.server-addr", "${jpower.".concat(profile).concat(".nacos.server-addr:}")); props.setProperty("spring.cloud.nacos.config.server-addr", "${jpower.".concat(profile).concat(".nacos.server-addr:}")); props.setProperty("spring.cloud.nacos.discovery.namespace", "${jpower.".concat(profile).concat(".nacos.namespace:}")); props.setProperty("spring.cloud.nacos.config.namespace", "${jpower.".concat(profile).concat(".nacos.namespace:}")); //sentinel配置 props.setProperty("spring.cloud.sentinel.transport.dashboard", "${jpower.".concat(profile).concat(".sentinel.dashboard:}")); //seata启用,默认关闭 props.setProperty("seata.enabled", "${jpower.seata.enabled:false}"); List<DeployService> deployServiceList = new ArrayList<>(); ServiceLoader.load(DeployService.class).forEach(deployServiceList::add); deployServiceList.stream().sorted(Comparator.comparing(DeployService::getOrder)).collect(Collectors.toList()) .forEach(deployService -> deployService.launcher(builder, appName, profile)); log.warn("{}项目已启动,运行环境:{}",appName,profile); return builder; } /** * @Author 郭丁志 * @Description //TODO 判断是否为本地开发环境 * @Date 18:18 2020-08-02 * @return boolean **/ public static boolean isLocalDev() { String osName = System.getProperty("os.name"); return StringUtils.hasText(osName) && !(AppConstant.OS_NAME_LINUX.equals(osName.toUpperCase())); } } ~~~ ## 如何使用 * 以`jpower-system`模块启动为例,代码如下 ~~~ /** * @ClassName SpringBootStartApplication * @Description TODO 系统管理入口 * @Author Ding * @Date 2020-02-24 18:41 * @Version 1.0 */ @EnableTransactionManagement @SpringBootApplication @EnableFeignClients public class SystemStartApplication { public static void main(String[] args) { JpowerApplication.run(AppConstant.JPOWER_SYSTEM,SystemStartApplication.class,args); } } ~~~ * 可以看到非常简约,与原生并没有太多变化,只是多了一个appName的参数,此参数正是做为服务名注册到注册中心,用于和其他服务分类。 ## 扩展启动参数 * 有时我们可能单独一个模块需要额外加载自己的配置,而其他模块则无需这些配置,jpower也想到了这种情况,我们只需要在所在模块继承`DeployService`接口并实现`launcher`方法即可。 示例代码如下: ~~~ /** * @ClassName DeployService * @Description TODO 扩展启动参数配置 * @Author 郭丁志 * @Date 2020-08-19 16:22 * @Version 1.0 */ public class DeployServiceImpl implements DeployService { @Override public void launcher(SpringApplicationBuilder builder, String appName, String profile) { Properties props = System.getProperties(); props.setProperty("spring.cloud.nacos.config.file-extension", NacosConstants.FILE_EXTENSION); props.setProperty("spring.cloud.nacos.config.shared-configs[0].data-id", NacosConstants.DATA_ID); props.setProperty("spring.cloud.nacos.config.shared-configs[0].refresh", NacosConstants.CONFIG_REFRESH); props.setProperty("spring.cloud.nacos.config.shared-configs[0].group", NacosConstants.CONFIG_GROUP); props.setProperty("spring.cloud.nacos.config.shared-configs[1].data-id", NacosConstants.nacosPublicDataId(profile)); props.setProperty("spring.cloud.nacos.config.shared-configs[1].refresh", NacosConstants.CONFIG_REFRESH); props.setProperty("spring.cloud.nacos.config.shared-configs[1].group", NacosConstants.CONFIG_GROUP); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } } ~~~ * 看到这里,可能大家也会有疑问,启动参数、公共参数写到DeployService里,这相当于是写死了,那我如果要修改,还得全部重新打包,这岂不是很不方便?我们的理念就是一次打包,处处运行,所以这些问题都能轻松解决 * 若是直接命令行运行,我们可以通过命令行的形式这么覆盖配置 > java -jar app.jar --spring.profiles.active=test --server.port=8080 * 若是通过docker-compose启动容器,那么我们可以这么覆盖配置 ~~~ jpower-system: image: "192.168.0.10:99/jpower/system-server:1.0.1-SNAPSHOT" privileged: true volumes: - /docker/skywalking/agent/:/jpower/skywalking/agent/ environment: - JAVA_TOOL_OPTIONS=-javaagent:/jpower/skywalking/agent/skywalking-agent.jar - SW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking-oap:11800 restart: always command: --spring.profiles.active=${PROFILE} --spring.cloud.nacos.discovery.ip=${ADMIN_IP} --spring.cloud.nacos.discovery.port=${ADMIN_PORT} --spring.cloud.sentinel.transport.port=${ADMIN_SENTINEL_PORT} --spring.cloud.nacos.config.namespace=${NACOS_NAMESPACE} --spring.cloud.nacos.config.group=${NACOS_GROUP} --spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR} --spring.cloud.nacos.discovery.server-addr=${NACOS_SERVER_ADDR} --spring.cloud.sentinel.transport.dashboard=${SENTINEL_DASHBOARD_ADDR} --sentinel.heartbeat.client.ip=${ADMIN_IP} networks: - jpower_net ~~~ ## 注意点 * 自定义启动器已经将环境变量也设置好,无需再到`application.yml`中配置`spring.profiles.active`再打包。 * 打包后的app启动时,若不设置`spring.profiles.active`,则默认为`dev`,如需设置只需在启动的命令行加上即可。 `java -jar app.jar --spring.profiles.active=prod --server.port=2333` * 无论是打包了fat-jar,还是打包了docker,都只需要打包一次,搭配上注册中心就可以运行在任何设定好的环境中,这样一来就实现了`一次打包,处处运行`的理念。 * 开发中,如果要修改为非`DEV`环境,可参考如下配置。 ![](https://img.kancloud.cn/d6/f4/d6f4ef28212ccd93bd441a2ee32090a9_2214x1424.png)