• springboot常用组件的集成


    目录

    springboot常用组件的集成

    1.创建项目

    2. web服务器配置

    3. 配置数据库

    4. 配置mybatis

    5. 开启事务

    6.aop配置

    7. pagehelper分页

    3. druid数据库连接池

    4. 集成redis

    编写一个controller用于测试

    2.手动装配redis


    1.创建项目

    1.idea创建项目

    创建步骤 :File --> New --> project,,,-->Spring initializr-->选择项目所需要的架包

    项目创建完成后可以查看pom.xml文件,上面选择的的第三方组件已经加入到pom.xml中了。

    pom.xml:

    1. <dependency>
    2. <groupId>org.mybatis.spring.bootgroupId>
    3. <artifactId>mybatis-spring-boot-starterartifactId>
    4. <version>2.1.1version>
    5. dependency>
    6. <dependency>
    7. <groupId>mysqlgroupId>
    8. <artifactId>mysql-connector-javaartifactId>
    9. <version>8.0.30version>
    10. dependency>

    2. web服务器配置

    打开application.properties文件

    1. #端口号
    2. server.port=8080
    3. #指定上下文路径
    4. server.servlet.context-path=/
    5. #指定url编码
    6. server.tomcat.uri-encoding=utf-8

    3. 配置数据库

    打开application.properties文件

    1. #驱动
    2. spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
    3. #数据库连接
    4. spring.datasource.url = jdbc:mysql://127.0.0.1:3306/db_text?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
    5. #用户名
    6. spring.datasource.username = root
    7. #密码
    8. spring.datasource.password = 123456

    mybatas-plus配置

     

    4. 配置mybatis

    打开application.properties文件

    1. #mybatis核心配置文件
    2. mybatis.config-locations=classpath:mybatis-config.xml
    3. #mybatis xml配置文件的位置
    4. mybatis.mapper-locations=classpath:/mapper/**/*.xml
    5. #在控制台输出执行的sql语句
    6. mybatis.configuration.logimpl=org.apache.ibatis.logging.stdout.StdOutImpl
    mybatis-config.xml文件内容:
    1. "1.0" encoding="UTF-8"?>
    2. configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    3. <configuration>
    4. <settings>
    5. <setting name="logImpl" value="SLF4J"/>
    6. settings>
    7. <typeAliases>
    8. <typeAlias alias="Integer" type="java.lang.Integer" />
    9. <typeAlias alias="String" type="java.lang.String"/>
    10. <typeAlias alias="Long" type="java.lang.Long" />
    11. <typeAlias alias="HashMap" type="java.util.HashMap" />
    12. <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
    13. <typeAlias alias="ArrayList" type="java.util.ArrayList" />
    14. <typeAlias alias="LinkedList" type="java.util.LinkedList" />
    15. typeAliases>
    16. <plugins>
    17. <plugin interceptor="com.github.pagehelper.PageInterceptor">
    18. plugin>
    19. plugins>
    20. configuration>

    5. 开启事务

    在启动类上加入如下注解:

     

    在需要进行事务管理的类或方法上加入事务注解就可以了(@Transactional)

    6.aop配置

    1. <dependency>
    2.   <groupId>org.springframework.bootgroupId>
    3.   <artifactId>spring-boot-starter-aopartifactId>
    4. dependency>

    7. pagehelper分页

    1. pom.xml

    1. <dependency>
    2.   <groupId>com.github.pagehelpergroupId>
    3.   <artifactId>pagehelper-spring-boot-starterartifactId>
    4.   <version>1.2.12version>
    5. dependency>

    2)application.properties

    1. # -------------------- pagehelper B ---------------------------
    2. pagehelper.helper-dialect= mysql
    3. #pagehelper.reasonable=true
    4. #pagehelper.support-methods-arguments=true
    5. #pagehelper.params=count=countSql
    6. # -------------------- pagehelper E ---------------------------
    1. 将课件提供的PageBean.java, PagingInterceptor.java,

    1.定义注解

    1. package com.example.springboot1.annotation;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. /**
    7. * @author L
    8. * @site www.xiaomage.com
    9. * @company xxx公司
    10. * @create  2022-06-10 9:10
    11. */
    12. @Retention(RetentionPolicy.RUNTIME)
    13. @Target(ElementType.METHOD)
    14. public @interface Paging {
    15. }

    2.定义切面

    1. package com.example.springboot1.aop;
    2. import com.example.springboot1.utils.PageBean;
    3. import com.github.pagehelper.PageHelper;
    4. import com.github.pagehelper.PageInfo;
    5. import org.aspectj.lang.ProceedingJoinPoint;
    6. import org.aspectj.lang.annotation.Around;
    7. import org.aspectj.lang.annotation.Aspect;
    8. import org.springframework.core.annotation.Order;
    9. import org.springframework.stereotype.Component;
    10. import java.util.List;
    11. /**
    12. * @author L
    13. * @site www.xiaomage.com
    14. * @company xxx公司
    15. * @create  2022-06-09 16:15
    16. */
    17. @Component
    18. @Aspect
    19. @Order(1)
    20. public class PagingAOP {
    21.   //@Around("execution(* com.zking.mybatisdemo..*.*Page(..))")
    22.   @Around("@annotation(com.example.springboot1.annotation.Paging)")
    23.   public Object around(ProceedingJoinPoint point) throws Throwable {
    24.       Object[] args = point.getArgs();
    25.       PageBean pageBean = null;
    26.       for(Object arg: args) {
    27.           if(arg instanceof PageBean) {
    28.               pageBean = (PageBean)arg;
    29.               if(pageBean != null && pageBean.isPagination()) {
    30.                   PageHelper.startPage(pageBean.getPage(), pageBean.getRows());
    31.               }
    32.           }
    33.       }
    34.       Object rv = point.proceed();
    35.       if(pageBean != null && pageBean.isPagination()) {
    36.           PageInfo info = new PageInfo((List)rv);
    37.           pageBean.setTotal(Long.valueOf(info.getTotal()).intValue());
    38.       }
    39.       return rv;
    40.   }
    41. }

    3.定义分页工具类

    1. package com.example.springboot1.utils;
    2. import com.mysql.cj.util.StringUtils;
    3. import javax.servlet.http.HttpServletRequest;
    4. import java.util.Map;
    5. public class PageBean {
    6. /**
    7. * 页码
    8. */
    9. private int page = 1;
    10. /**
    11. * 每页显示的记录数
    12. */
    13. private int rows = 10;
    14. /**
    15. * 总记录数
    16. */
    17. private int total = 0;
    18. /**
    19. * 是否分页
    20. */
    21. private boolean pagination = true;
    22. /**
    23. * 记录查询的url,以便于点击分页时再次使用
    24. */
    25. private String url;
    26. /**
    27. * 存放请求参数,用于生成隐藏域中的元素
    28. */
    29. private Map parameterMap;
    30. /**
    31. * 根据传入的Request初始化分页对象
    32. * @param request
    33. */
    34. public void setRequest(HttpServletRequest request) {
    35. if(!StringUtils.isNullOrEmpty(request.getParameter("page"))) {
    36. this.page = Integer.valueOf(request.getParameter("page"));
    37. }
    38. if(!StringUtils.isNullOrEmpty(request.getParameter("rows"))) {
    39. this.rows = Integer.valueOf(request.getParameter("rows"));
    40. }
    41. if(!StringUtils.isNullOrEmpty(request.getParameter("pagination"))) {
    42. this.pagination = Boolean.valueOf(request.getParameter("pagination"));
    43. }
    44. this.url = request.getRequestURI();
    45. this.parameterMap = request.getParameterMap();
    46. request.setAttribute("pageBean", this);
    47. }
    48. public int getPage() {
    49. return page;
    50. }
    51. public void setPage(int page) {
    52. this.page = page;
    53. }
    54. public int getRows() {
    55. return rows;
    56. }
    57. public void setRows(int rows) {
    58. this.rows = rows;
    59. }
    60. public int getTotal() {
    61. return total;
    62. }
    63. public void setTotal(int total) {
    64. this.total = total;
    65. }
    66. public boolean isPagination() {
    67. return pagination;
    68. }
    69. public void setPagination(boolean pagination) {
    70. this.pagination = pagination;
    71. }
    72. public String getUrl() {
    73. return url;
    74. }
    75. public void setUrl(String url) {
    76. this.url = url;
    77. }
    78. public Map getParameterMap() {
    79. return parameterMap;
    80. }
    81. public void setParameterMap(Map parameterMap) {
    82. this.parameterMap = parameterMap;
    83. }
    84. //计算起始页码
    85. public int getStartIndex() {
    86. return (this.page - 1) * this.rows;
    87. }
    88. //获取总页数
    89. public int getTotalPage() {
    90. if (this.getTotal() % this.rows == 0) {
    91. return this.getTotal() / this.rows;
    92. } else {
    93. return this.getTotal() / this.rows + 1;
    94. }
    95. }
    96. //上一页
    97. public int getPreviousPage() {
    98. return this.page - 1 > 0 ? this.page - 1 : 1;
    99. }
    100. //下一页
    101. public int getNextPage() {
    102. return this.page + 1 > getTotalPage() ? getTotalPage() : this.page + 1;
    103. }
    104. }

    4)集成结束,可以编写测试方法进行测试

     

    然后调用方法测试即可

    注意此处 ,如使用两次界面 paging需要在最上方 否则失效

    3. druid数据库连接池

    阿里开源的数据库连接池,使用java开发,提供强大的监控和扩展功能,可以替换DBCP和C3P0连接池,性能要比其他的连接池要好。 1)pom.xml

    1. <dependency>
    2.   <groupId>com.alibabagroupId>
    3.   <artifactId>druid-spring-boot-starterartifactId>
    4.   <version>1.1.21version>
    5. dependency>
    1. application.properties

    1. #--------------------- druid config B ------------------------
    2. #config druid
    3. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    4. #初始化时建立物理连接的个数
    5. spring.datasource.druid.initial-size=5
    6. #最小连接池数量
    7. spring.datasource.druid.min-idle=5
    8. #最大连接池数量 maxIdle已经不再使用
    9. spring.datasource.druid.max-active=20
    10. #获取连接时最大等待时间,单位毫秒
    11. spring.datasource.druid.max-wait=60000
    12. #申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
    13. spring.datasource.druid.test-while-idle=true
    14. #既作为检测的间隔时间又作为testWhileIdel执行的依据
    15. spring.datasource.druid.time-between-eviction-runs-millis=60000
    16. #销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接
    17. spring.datasource.druid.min-evictable-idle-time-millis=30000
    18. #用来检测连接是否有效的sql 必须是一个查询语句
    19. #mysql中为 select 1
    20. #oracle中为 select 1 from dual
    21. spring.datasource.druid.validation-query=select 1
    22. #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
    23. spring.datasource.druid.test-on-borrow=false
    24. #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
    25. spring.datasource.druid.test-on-return=false
    26. #当数据库抛出不可恢复的异常时,抛弃该连接
    27. #spring.datasource.druid.exception-sorter=true
    28. #是否缓存preparedStatement,mysql5.5+建议开启
    29. spring.datasource.druid.pool-prepared-statements=true
    30. #当值大于0时poolPreparedStatements会自动修改为true
    31. spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
    32. #配置扩展插件
    33. spring.datasource.druid.filters=stat,wall
    34. #通过connectProperties属性来打开mergeSql功能;慢SQL记录
    35. spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    36. #合并多个DruidDataSource的监控数据
    37. spring.datasource.druid.use-global-data-source-stat=true
    38. # WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter
    39. #是否启用StatFilter默认值true
    40. spring.datasource.druid.web-stat-filter.enabled=true
    41. spring.datasource.druid.web-stat-filter.url-pattern=/*
    42. #经常需要排除一些不必要的url,比如*.js,/jslib/*等等
    43. spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
    44. #Druid内置提供了一个StatViewServlet用于展示Druid的统计信息
    45. #设置访问druid监控页的账号和密码,默认没有
    46. spring.datasource.druid.stat-view-servlet.enabled=true
    47. spring.datasource.druid.stat-view-servlet.reset-enable=false
    48. spring.datasource.druid.stat-view-servlet.login-username=admin
    49. spring.datasource.druid.stat-view-servlet.login-password=admin
    50. #DruidStatView的servlet-mapping
    51. spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
    52. #允许列表,只有配置在此处的ip才允许访问durid监控平台
    53. spring.datasource.druid.stat-view-servlet.allow=127.0.0.1
    54. #拒绝列表,配置下此处的ip将被拒绝访问druid监控平台
    55. spring.datasource.druid.stat-view-servlet.deny=
    56. #--------------------- druid config E ------------------------

    application.yml配置

    1. spring:
    2. datasource:
    3.   url: jdbc:mysql://ip:port/数据库?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
    4.   username: root
    5.   password:
    6.   driver-class-name: com.mysql.jdbc.Driver
    7.   type: com.alibaba.druid.pool.DruidDataSource
    8.   druid:
    9.      #初始化大小
    10.     initialSize: 5
    11.      #最小值
    12.     minIdle: 5
    13.      #最大值
    14.     maxActive: 20
    15.      #最大等待时间,配置获取连接等待超时,时间单位都是毫秒ms
    16.     maxWait: 60000
    17.      #配置间隔多久才进行一次检测,检测需要关闭的空闲连接
    18.     timeBetweenEvictionRunsMillis: 60000
    19.      #配置一个连接在池中最小生存的时间
    20.     minEvictableIdleTimeMillis: 300000
    21.     validationQuery: SELECT 1 FROM DUAL
    22.     testWhileIdle: true
    23.     testOnBorrow: false
    24.     testOnReturn: false
    25.     poolPreparedStatements: true
    26.      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,
    27.      #'wall'用于防火墙,SpringBoot中没有log4j,我改成了log4j2
    28.     filters: stat,wall,log4j2
    29.      #最大PSCache连接
    30.     maxPoolPreparedStatementPerConnectionSize: 20
    31.     useGlobalDataSourceStat: true
    32.      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    33.     connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    34.      # 配置StatFilter
    35.     web-stat-filter:
    36.        #默认为false,设置为true启动
    37.       enabled: true
    38.       url-pattern: "/*"
    39.       exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
    40.      #配置StatViewServlet
    41.     stat-view-servlet:
    42.       url-pattern: "/druid/*"
    43.        #允许那些ip
    44.       allow: 127.0.0.1
    45.       login-username: admin
    46.       login-password: 123456
    47.        #禁止那些ip
    48.       deny: 192.168.1.102
    49.        #是否可以重置
    50.       reset-enable: true
    51.        #启用
    52.       enabled: true

    4. 集成redis

    1. pom.xml

    1. <dependency>
    2.   <groupId>org.springframework.bootgroupId>
    3.   <artifactId>spring-boot-starter-data-redisartifactId>
    4. dependency>
    1. application.properties

    1. # -------------------- redis config B -------------------------
    2. # Redis数据库索引(默认为0)
    3. spring.redis.database=0
    4. # Redis服务器地址
    5. spring.redis.host=192.168.0.24
    6. # Redis服务器连接端口
    7. spring.redis.port=6379
    8. # Redis服务器连接密码(默认为空)
    9. spring.redis.password=
    10. # 连接池最大连接数(使用负值表示没有限制)
    11. spring.redis.jedis.pool.max-active=100
    12. # 连接池最大阻塞等待时间(使用负值表示没有限制)
    13. spring.redis.jedis.pool.max-wait=-1ms
    14. # 连接池中的最大空闲连接
    15. spring.redis.jedis.pool.max-idle=10
    16. # 连接池中的最小空闲连接
    17. spring.redis.jedis.pool.min-idle=0
    18. # 连接超时时间(毫秒)
    19. spring.redis.jedis.timeout=1000
    20. # -------------------- redis config E -------------------------

    yml文件配置

    1. redis:
    2.   database: 1
    3.   host: 120.79.61.66   # Redis服务器地址
    4.   port: 6379           # Redis服务器连接端口
    5.   password: 123456     # Redis服务器连接密码(默认为空)
    6.   jedis:
    7.     pool:
    8.       max-active: 200     # 连接池最大连接数(使用负值表示没有限制)
    9.       max-wait: -1       # 连接池最大阻塞等待时间(使用负值表示没有限制)
    10.       max-idle: 10       # 连接池中的最大空闲连接
    11.       min-idle: 2         # 连接池中的最小空闲连接
    12.   connect-timeout: 6000   # 连接超时时间(毫秒)

    1. 编写一个controller用于测试

    1. /**
    2. * 用于测试redis集成
    3. * @author Administrator
    4. * @create 2019-12-1822:16
    5. */
    6. @RestController
    7. public class RedisTestController {
    8.   @Resource
    9.   private RedisTemplate redisTemplate;
    10.   @RequestMapping(value = "/redis")
    11.   public Object redis() {
    12.       String name = "redis test";
    13.       redisTemplate.opsForValue().set("redisTest", name);
    14.       Map map = new HashMap<>();
    15.       map.put("code", 1);
    16.       map.put("msg", "操作成功");
    17.       return map;
    18.   }
    19. }

    可以通过postman进行测试,如果正常在redis中添加key,则说明集成成功。

    配置文件中需要写入redis基本配置

    1. redis:
    2.   database: 1
    3.   host: 120.79.61.66   # Redis服务器地址
    4.   port: 6379           # Redis服务器连接端口
    5.   password: 123456     # Redis服务器连接密码(默认为空)
    6.   jedis:
    7.     pool:
    8.       max-active: 200     # 连接池最大连接数(使用负值表示没有限制)
    9.       max-wait: -1       # 连接池最大阻塞等待时间(使用负值表示没有限制)
    10.       max-idle: 10       # 连接池中的最大空闲连接
    11.       min-idle: 2         # 连接池中的最小空闲连接
    12.   connect-timeout: 6000   # 连接超时时间(毫秒)

    手动配置后 利用json进行转换 所需架包

    1.   <dependency>
    2.     <groupId>com.fasterxml.jackson.coregroupId>
    3.     <artifactId>jackson-databindartifactId>
    4.     <version>2.13.3version>
    5.   dependency>

    2.手动装配redis

    1. package com.example.springboot1.confing;
    2. import com.fasterxml.jackson.annotation.JsonAutoDetect;
    3. import com.fasterxml.jackson.annotation.PropertyAccessor;
    4. import com.fasterxml.jackson.databind.ObjectMapper;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.springframework.boot.context.properties.ConfigurationProperties;
    7. import org.springframework.cache.CacheManager;
    8. import org.springframework.context.annotation.Bean;
    9. import org.springframework.context.annotation.Configuration;
    10. import org.springframework.data.redis.cache.RedisCacheConfiguration;
    11. import org.springframework.data.redis.cache.RedisCacheManager;
    12. import org.springframework.data.redis.connection.RedisConnectionFactory;
    13. import org.springframework.data.redis.core.RedisTemplate;
    14. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    15. import org.springframework.data.redis.serializer.RedisSerializationContext;
    16. import org.springframework.data.redis.serializer.RedisSerializer;
    17. import org.springframework.data.redis.serializer.StringRedisSerializer;
    18. import java.time.Duration;
    19. /**
    20. * 配置rdeis 中的数据是 json 加入保存
    21. */
    22. @Configuration
    23. @ConfigurationProperties(prefix = "spring.cache.redis")
    24. @Slf4j
    25. public class RedisCacheConfig {
    26.   private Duration timeToLive = Duration.ZERO;
    27.   public void setTimeToLive(Duration timeToLive) {
    28.       this.timeToLive = timeToLive;
    29.   }
    30.   @Bean
    31.   public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
    32.       // 创建redisTemplate
    33.       RedisTemplate redisTemplate = new RedisTemplate<>();
    34.       redisTemplate.setConnectionFactory(connectionFactory);
    35.       // 使用Jackson2JsonRedisSerialize替换默认序列化
    36.       Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    37.       ObjectMapper objectMapper = new ObjectMapper();
    38.       objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    39.       objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    40.       jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    41.       // key采用String的序列化方式
    42.       redisTemplate.setKeySerializer(new StringRedisSerializer());
    43.       // value序列化方式采用jackson
    44.       redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    45.       // hash的key也采用String的序列化方式
    46.       redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    47.       // hash的value序列化方式采用jackson
    48.       redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    49.       redisTemplate.afterPropertiesSet();
    50.       return redisTemplate;
    51.   }
    52.   @Bean
    53.   public CacheManager cacheManager(RedisConnectionFactory factory) {
    54.       RedisSerializer redisSerializer = new StringRedisSerializer();
    55.       Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    56.       //解决查询缓存转换异常的问题
    57.       ObjectMapper om = new ObjectMapper();
    58.       om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    59.       om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    60.       jackson2JsonRedisSerializer.setObjectMapper(om);
    61.       // 配置序列化(解决乱码的问题)
    62.       RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    63.               .entryTtl(timeToLive)
    64.               .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
    65.               .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
    66.               .disableCachingNullValues();
    67.       RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
    68.               .cacheDefaults(config)
    69.               .build();
    70.       return cacheManager;
    71.   }
    72. }

  • 相关阅读:
    “免费项目管理软件”20款大盘点!你认识哪几款?
    NMS技术总结(NMS原理、多类别NMS、NMS的缺陷、NMS的改进思路、各种NMS方法)
    Android Studio Flutter真机调试错误
    MySQL数据库管理基本操作
    想学嵌入式开发,薪资怎么样?
    LeetCode --- 1266. Minimum Time Visiting All Points 解题报告
    java计算机毕业设计ssm+jsp仓库管理系统
    Linux ARM平台开发系列讲解(调试篇) 1.3.2 RK3399移植Ubuntu文件系统步骤
    2020年全国职业院校技能竞赛网络安全数据包分析capture.pcapng
    HarmonyOS 4.0 实况窗上线!支付宝实现医疗场景智能提醒
  • 原文地址:https://blog.csdn.net/qq_62898618/article/details/127872263