
# 开发业务代码
#### 准备工作
- 已[下载](https://maven.apache.org/download.cgi)并[安装](https://maven.apache.org/install.html)Maven，根据以下步骤配置Maven。
  1. 在\<localRepository\>标签内添加自己的本地仓库位置路径，这个本地仓库位置是自己创建的。 D:\\apache-maven-3.8.6-bin\\repository
     ```
     <localRepository>D:\apache-maven-3.8.6-bin\repository</localRepository>
     ```
     
  
  2. 修改maven默认的JDK版本。 在\<profiles\>标签下添加一个\<profile\>标签，修改maven默认的JDK版本。
     ```
     <profile>     
         <id>JDK-1.8</id>       
         <activation>       
             <activeByDefault>true</activeByDefault>       
             <jdk>1.8</jdk>       
         </activation>       
         <properties>       
             <maven.compiler.source>1.8</maven.compiler.source>       
             <maven.compiler.target>1.8</maven.compiler.target>       
             <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>       
         </properties>       
     </profile>
     ```
     
   
- 安装并配置IntelliJ IDEA开发工具。
  1. 在IntelliJ IDEA中选择File \> Settings \> Build,Execution,Deployment \> Build Tools \> Maven。
  
  2. 在User settings file中配置setting.xml。
  
  3. 在Local repository中配置自定义的Maven仓库地址。
   
- JAVA开发环境的配置。 AppStage提供的SDK是基于java1.8版本开发的，如果Spring Cloud项目使用java11及以上版本，则不支持使用AppStage提供的SDK进行应用开发。以下步骤以win7环境配置JDK8 64位为例，如果已经下载JDK并配置好环境请跳过本步骤。
  1. 下载JDK文件。
  
  2. 下载完成后按照提示安装，位置自选，比如安装到本地C:\\Program Files\\Java\\jdk1.8.0_131。
  
  3. 配置Java环境变量，进行如下操作。
     1. 新建系统变量JAVA_HOME，变量值为实际JDK安装位置。
     
     2. 在Path中添加%JAVA_HOME%\\bin;%JAVA_HOME%\\jre\\bin （注意用英文分号分隔）。
     
     3. 新建系统变量CLASSPATH，变量值为%JAVA_HOME%\\lib\\dt.jar;%JAVA_HOME%\\lib\\tools.jar。
     
     4. 打开命令行窗口，输入"java -version"，显示如[图1]表示配置成功。
        图1配置成功示例   
        ![](https://support.huaweicloud.com/bestpractice-appstage/zh-cn_image_0000001785345040.png "点击放大") 
      
   
- 获取SDK并进行完整性校验。
  - SDK：[nuwa-open-sdk-1.1.0-20240204093135.zip](https://appstage-opensdk-cn4.obs.cn-north-4.myhuaweicloud.com/nuwa-open-sdk/1.1.0/202402020945/nuwa-open-sdk-1.1.0-20240204093135.zip)
  
  - 完整性校验：[nuwa-open-sdk-1.1.0-20240204093135.zip.sha256](https://appstage-opensdk-cn4.obs.cn-north-4.myhuaweicloud.com/nuwa-open-sdk/1.1.0/202402020945/nuwa-open-sdk-1.1.0-20240204093135.zip.sha256)
   
 
#### 操作步骤
1. 打开本地Spring Cloud项目。
2. 手动导入SDK jar包。 
   1. 在项目目录下新建一个lib目录，存放jar包。
   
   2. 将本地的jar包复制粘贴至lib目录下。
   
   3. 将jar导入到项目中。
      1. 选择"File \> Project Structure \> Project Settings \> Module"。
      
      2. 单击"+"，选择"JARs or Directories..."。
      
      3. 选中jar包，单击"apply"。导包完成。
       
   
   
   
   
3. （可选）集成OrgID登录功能。 
   DemoOrgidLogin项目用于对接OrgID组织成员账号服务，对接后可以使用OrgID服务完成自身应用的登录、组织管理功能。具体请参考[集成OrgID登录功能](https://support.huaweicloud.com/devg-appstage/appstage_06_0066.html)。
   
   
4. 配置微服务。 
   1. 微服务接入STS。 STS提供了微服务之间请求认证以及敏感配置项管理的功能，STS是接入Cloud Map的前提条件，Cloud Map依赖STS认证能力。
      1. 将在AppStage首页中创建的DemoOrgidLogin、DemoServiceAService、DemoServiceBService注册到运维中心的ACMS管理台中，具体请参见[将微服务注册到ACMS](https://support.huaweicloud.com/usermanual-wiseeye/appstage_04_0478.html)。
         图2注册微服务   
         ![](https://support.huaweicloud.com/bestpractice-appstage/zh-cn_image_0000001848961165.png "点击放大")
         
         
      
      2. 在DemoOrgidLogin、DemoServiceA、DemoServiceB的pom.xml中添加STS依赖。 如果将SDK放到外部maven仓中，则只需要添加sts-spring-boot依赖。
         本例是以本地依赖的方式引入SDK，即手动将本地下载的SDK jar包引入到工程的lib目录下，需要添加的依赖如下所示。实际需要依赖的包只有sts-spring-boot，其他均是间接依赖，如果启动过程中需要其他开源依赖也需要手动加入。
         ```
         <dependency>      
              <groupId>com.huawei.wisecloud.sts</groupId>
              <artifactId>sts-spring-boot</artifactId>
              <version>1.1.19.100</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/sts-spring-boot-1.1.19.100.jar</systemPath>
         </dependency>
         <dependency>      
              <groupId>com.huawei.wisecloud.sts</groupId>
              <artifactId>sts-key-sdk</artifactId>
              <version>1.1.19.100</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/sts-key-sdk-1.1.19.100.jar</systemPath>
         </dependency>
         <dependency>      
              <groupId>com.huawei.wisecloud.sts</groupId>
              <artifactId>sts-sdk-base</artifactId>
              <version>1.1.19.100</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/sts-sdk-base-1.1.19.100.jar</systemPath>
         </dependency>
         <dependency>      
              <groupId>com.huawei.wisecloud.kms</groupId>
              <artifactId>cloudsoa-security</artifactId>
              <version>1.1.14.101</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/cloudsoa-security-1.1.14.101.jar</systemPath>
         </dependency>
         ```
         
      
      3. 增加STS配置项。 在DemoOrgidLogin、DemoServiceA、DemoServiceB的ClassPath下增加application.properties配置文件，本实践配置项如下：
         ```
         sts.server.domain=10.33.102.162:8080      #STS接入地址
         sts.config.path=/opt/huawei/certs/jamesService22A/DemoServiceAService/DemoServiceAService.ini       #STS微服务证书路径，IaC3.0会将证书放在固定路径下，格式为/opt/huawei/certs/服务名/微服务名/微服务名.ini
         ```
         
      
      4. 启动STS。 在微服务的启动类中添加注解，本实践是在ServiceASpringbootApplication.java、ServiceBSpringbootApplication.java和DemoOrgidLogin的OrgLogin.java文件中添加@EnableStsAutoInitialization(value = "application.properties")注解。注解的含义是启动STS自动初始化，其中value是指定STS的配置文件路径。
         
       
   
   2. 敏感信息配置项托管。
      1. 使用STS的敏感配置项管理功能 ，需要在ACMS中录入敏感配置项，具体请参见[录入敏感配置](https://support.huaweicloud.com/usermanual-wiseeye/appstage_04_0481.html)。
      
      2. 在IaC脚本中的业务配置项配置文件中指定敏感配置项取值路径。
      
      3. 在IaC脚本中的业务配置项属性定义文件中，声明该配置项为敏感配置项。
      
      4. 在配置文件中增加敏感配置项名称的配置。
      
      5. 启动敏感配置项自动解密。
      
      
      使用OrgID登录时所需的客户端密钥，属于敏感配置项，需要使用STS的配置敏感配置项托管功能。本实践中配置如下：
      1. 在ACMS中录入配置敏感配置项，如[图3]所示。
         图3录入敏感配置项   
         ![](https://support.huaweicloud.com/bestpractice-appstage/zh-cn_image_0000001804117726.png "点击放大")
         
         
      
      2. 本实践在IaC脚本DemoOrgidLogin的业务配置项文件config_records.yaml增加一个名为spring.redis.password的敏感配置项，这个敏感配置项是访问Redis的密码。
         ```
         spring.redis.password: MicroService/jamesService22A/DemoOrgidLogin/spring.redis.password/default  #Redis访问密码，属于敏感配置项，已被STS(ACMS)纳管，配置路径为：MicroService/服务名/微服务名/敏感配置项名称/敏感配置项标签
         ```
         
      
      3. 本实践中访问Redis的密码spring.redis.password为敏感配置项，在IaC脚本DemoOrgidLogin的业务配置项属性定义文件config_schema.yaml中声明。
         ```
         type: object
         properties:
            spring.redis.password:
              format: sensitive
         ```
         
      
      4. 在application.yml配置文件中增加敏感配置项名称的配置。
         ```
         nuwa:
            security:
              config:
                sensitiveWords: spring.redis.password,org.app.protocol-login.oauth.clientSecret,org.app.jwt-key
         ```
         
      
      5. 在DemoOrgidLogin的启动类中添加注解，启动敏感配置项自动解密。本实践是在OrgLogin.java文件中添加@EnableStsEncryptableProperties注解。
        
   
   3. 微服务接入Cloud Map。
      Spring Cloud通常是使用其自带的Eureka注册中心，接入AppStage可以将Eureka注册中心替换为Cloud Map，Cloud Map其优势为除了能够提供服务发现的功能，还可以提供数据库、敏感信息等的纳管功能。
      1. 在DemoOrgidLogin、DemoServiceA、DemoServiceB的pom.xml中添加Cloud Map依赖。 如果将SDK放到外部maven仓中，则只需要添加nuwa-cloudmap-core依赖。
         本例是以本地依赖的方式引入SDK，即手动将本地下载的SDK jar包引入到工程的lib目录下，需要添加的依赖如下所示。实际需要依赖的包只有nuwa-cloudmap-core，其他均是间接依赖，如果启动过程中需要其他开源依赖也需要手动加入。
         ```
         <dependency>     
              <groupId>com.huawei.wisecloud.nuwa</groupId>
              <artifactId>spring-cloud-starter-cloudmap-discovery</artifactId>
              <version>1.0.12.100</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/spring-cloud-starter-cloudmap-discovery-1.0.12.100.jar</systemPath>
         </dependency>
         <dependency>     
              <groupId>com.huawei.wisecloud.nuwa</groupId>
              <artifactId>nuwa-cloudmap-core</artifactId>
              <version>1.0.12.100</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/nuwa-cloudmap-core-1.0.12.100.jar</systemPath>
         </dependency>
         <dependency>     
              <groupId>com.huawei.wisecloud.nuwa</groupId>
              <artifactId>nuwa-cloudmap-spring-boot-starter</artifactId>
              <version>1.0.12.100</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/nuwa-cloudmap-spring-boot-starter-1.0.12.100.jar</systemPath>
         </dependency>
         ```
         
      
      2. 增加Cloud Map配置项。 本实践分别在DemoServiceA、DemoServiceB的application.yaml配置文件及DemoOrgidLogin的application.yml文件中添加以下配置项：
         ```
         nuwa:
           cloudmap:
             read: cloudmap                              #使用Cloud Map方式进行微服务间通信
             clusterName: SpringCloudDemo                #微服务注册到Cloud Map的集群 
             provider:
               cluster: SpringCloudDemo                  #提供服务的其他同样被注册到Cloud Map的微服务集群名 
             serverAddr: http://10.34.32.243:80          #Cloud Map访问地址
             version: 1.0.0.100                          #微服务版本号
             namespaceName: cn_dev_default               #Cloud Map访问命名空间
         ```
         
      
      3. 启动初始化Cloud Map，将微服务注册到Cloud Map。 在微服务的启动类中添加注解，本实践是在ServiceASpringbootApplication.java、ServiceBSpringbootApplication.java和DemoOrgidLogin的OrgLogin.java文件中添加@EnableDiscoveryClient注解。同时在启动类中完成将RestTemplate注册到spring容器中，后续微服务间调用使用注册到spring容器中的RestTemplate，代码如下：
         ```
         package com.huawei.demo.servicea;
         import org.springframework.boot.SpringApplication;
         import org.springframework.boot.autoconfigure.SpringBootApplication;
         import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
         import org.springframework.cloud.client.loadbalancer.LoadBalanced;
         import org.springframework.context.annotation.Bean;
         import org.springframework.web.client.RestTemplate;
         import com.huawei.wisesecurity.sts.springboot.security.annotation.EnableStsAutoInitialization;
         @SpringBootApplication
         @EnableStsAutoInitialization(value = "application.properties")
         @EnableDiscoveryClient
         public class ServiceASpringbootApplication {
             public static void main(String[] args) {
                 SpringApplication.run(ServiceASpringbootApplication.class, args);
             }
             @Bean
             @LoadBalanced
             public RestTemplate restTemplate() {
                 return new RestTemplate();
             }
         }
         ```
         微服务间调用：
         ```
         public OrderInfo findOrder(String orderId, HttpServletRequest request) {
                 OrderInfo orderInfo = orderMapper.getOrderById(orderId);
                 String url = demoServiceAUrl + "/user/" + orderInfo.getUserId();
                 HttpHeaders headers = createHeaders(request);
                 HttpEntity<String> entity = new HttpEntity<>(null, headers);
                 ResponseEntity<UserInfo> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, UserInfo.class); ;
                 if (responseEntity != null) {
                     orderInfo.setUserInfo(responseEntity.getBody());
                 }
                 return orderInfo;
             }
         ```
         
      
      4. 在AppStage运维中心的Cloud Map中验证微服务注册，具体请参见[查看注册到CloudMap的微服务列表](https://support.huaweicloud.com/usermanual-wiseeye/appstage_04_0404.html)。
         图4验证微服务注册   
         ![](https://support.huaweicloud.com/bestpractice-appstage/zh-cn_image_0000001804248632.png "点击放大")
         
         
        
   
   4. 使用WiseDBA进行数据库纳管。
      1. 在AppStage运维中心的WiseDBA中申请数据库并创建Schema，具体请参见创建数据库实例及[在WiseDBA中创建Schema](https://support.huaweicloud.com/usermanual-wiseeye/appstage_04_0434.html)。
         本实践中申请的数据库性能规格为：独享型/gaussdb.mysql.large.x86.8/2vCPUs/16GB，数据库实例名称为：gauss-SpringCloudDemo2，DemoServiceAService和DemoServiceBService的Schema名称分别为：springclouddemoa、springclouddemob。
         
      
      2. 在WiseDBA管理台配置连接信息，并注册到Cloud Map中，在创建数据库连接时，直接调用Cloud Map中的注册信息。
         单击WiseDBA实例列表中的数据库实例名称，单击左侧导航栏的"SDK配置"，新增微服务和数据库的对应配置，具体请参见[新增数据库SDK配置并注册到Cloud Map](https://support.huaweicloud.com/usermanual-wiseeye/appstage_04_0445.html)，本实践中配置如[图5]所示。
         图5SDK配置   
         ![](https://support.huaweicloud.com/bestpractice-appstage/zh-cn_image_0000001836098222.png "点击放大") 
      
      3. 在DemoServiceA和DemoServiceB的pom.xml中添加Rainbow SDK依赖。 如果将SDK放到外部maven仓中，则只需要添加rainbow-proxy依赖。
         本例是以本地依赖的方式引入SDK，即手动将本地下载的SDK jar包引入到工程的lib目录下，需要添加的依赖如下所示。实际需要依赖的包只有rainbow-proxy，其他均是间接依赖，如果启动过程中需要其他开源依赖也需要手动加入。
         ```
         <dependency>          
              <groupId>com.huawei.wisecloud.nuwa</groupId>
              <artifactId>rainbow-proxy</artifactId>
              <version>1.2.18.102</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/rainbow-proxy-1.2.18.102.jar</systemPath>
         </dependency>
         <dependency>          
              <groupId>com.huawei.wisecloud.nuwa</groupId>
              <artifactId>rainbow-core-drds</artifactId>
              <version>1.2.18.201</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/rainbow-core-drds-1.2.18.201.jar</systemPath>
         </dependency>
         <dependency>        
              <groupId>com.huawei.wisecloud.gpaas</groupId>
              <artifactId>gpaas-middleware-common</artifactId>
              <version>2.2.6.100</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/gpaas-middleware-common-2.2.6.100.jar</systemPath>
         </dependency>
         ```
         
      
      4. 初始化Rainbow。
         在DemoServiceA、DemoServiceB的com/huawei/demo/servicea/config/下添加ApplicationAutoConfig.java文件，代码如下：
         ```
         package com.huawei.demo.serviceb.config;
         import javax.sql.DataSource;
         import org.mybatis.spring.SqlSessionFactoryBean;
         import org.mybatis.spring.mapper.MapperScannerConfigurer;
         import org.springframework.context.EnvironmentAware;
         import org.springframework.context.annotation.Bean;
         import org.springframework.context.annotation.Configuration;
         import org.springframework.context.annotation.DependsOn;
         import org.springframework.context.annotation.Import;
         import org.springframework.core.Ordered;
         import org.springframework.core.annotation.Order;
         import org.springframework.core.env.Environment;
         import org.springframework.jdbc.core.JdbcTemplate;
         import org.springframework.jdbc.datasource.DataSourceTransactionManager;
         import org.springframework.transaction.support.TransactionTemplate;
         import com.huawei.nuwa.map.client.NuwaMapClient;
         import com.huawei.nuwa.map.spring.boot.autoconfigure.NuwaCloudMapAutoConfiguration;
         import com.huawei.rainbow.jdbc.DbGroupDataSource;
         import com.huawei.wisesecurity.sts.springboot.security.configuration.StsEncryptablePropertiesConfiguration;
         /**
          * Rainbow启动
          */
         @Configuration
         @Order(Ordered.HIGHEST_PRECEDENCE)
         @Import(value = {NuwaCloudMapAutoConfiguration.class, StsEncryptablePropertiesConfiguration.class})
         public class ApplicationAutoConfig implements EnvironmentAware {
             private Environment environment;
             /**
              * Rainbow需要依赖STS和Cloud Map启动，创建dataSource
              *
              * @param client NuwaMapClient值对象
              * @return DataSource 初始化完成的数据源数据源
              */
             @Bean
             @DependsOn({"stsBootStrap"})
             public DataSource createDataSource(NuwaMapClient client) {
                 DbGroupDataSource dataSource = new DbGroupDataSource();
                 dataSource.setUseSts(true);
                 dataSource.setAppName(environment.getProperty("wiseDba.appName"));
                 dataSource.setDbGroupKey(environment.getProperty("wiseDba.groupKey"));
                 dataSource.setDbName(environment.getProperty("wiseDba.dbName"));
                 dataSource.init();
                 return dataSource;
             }
             /**
              * 事务管理创建
              *
              * @param dataSource 数据源
              * @return DataSourceTransactionManager 事务管理
              */
             @Bean
             public DataSourceTransactionManager createTransaction(DataSource dataSource) {
                 DataSourceTransactionManager transaction = new DataSourceTransactionManager();
                 transaction.setDataSource(dataSource);
                 return transaction;
             }
             @Bean
             public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
                 return new JdbcTemplate(dataSource);
             }
             @Bean("sqlSessionFactoryBean")
             public SqlSessionFactoryBean createMybatisSqlSessionFactoryBean(DataSource dataSource) {
                 SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
                 sessionFactoryBean.setDataSource(dataSource);
                 // 数据源配置项
                 org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
                 //允许jdbc自动生成主键
                 configuration.setUseGeneratedKeys(true);
                 // 使用列标签代替列名
                 configuration.setUseColumnLabel(true);
                 // 打开下划线命名自动转换为驼峰命名开关
                 configuration.setMapUnderscoreToCamelCase(true);
                 sessionFactoryBean.setConfiguration(configuration);
                 return sessionFactoryBean;
             }
             @Bean
             public TransactionTemplate createTransactionTemplate(DataSourceTransactionManager dataSourceTransactionManager) {
                 TransactionTemplate template = new TransactionTemplate();
                 template.setTransactionManager(dataSourceTransactionManager);
                 return template;
             }
             @Bean
             public MapperScannerConfigurer createMapperScannerConfigurer() {
                 MapperScannerConfigurer configurer = new MapperScannerConfigurer();
                 configurer.setBasePackage("com.huawei.demo");
                 configurer.setSqlSessionFactoryBeanName("sqlSessionFactoryBean");
                 return configurer;
             }
             @Override
             public void setEnvironment(Environment environment) {
                 this.environment = environment;
         ```
         
       
   
   
   
   
 
