Day08:SpringCloud知识梳理及Demo

一、知识梳理

在这里插入图片描述

  • 用户的所以请求首先通过api网关,Zuul。Zuul在我们的项目中主要用来转发请求链接。转发到内网中服务器,起到反向代理的作用,保证内网服务器不允许外部用户直接访问。网络比较安全。Zuul在application.yml文件中配置映射。
  • 用户请求就被转向业务消费者,消费者进过feign封装,更好的支持REST形式访问。具体我们做了一个接口,用来封装操作。UserFeign,也支持Ribbon,经过Ribbon本地动态网址,可以快速直接访问服务调用者,还可实现负载均衡,类似Nginx。
  • 经过Hystrix封装,整个架构也具有断路器功能,当业务访问正常时,断路器不工作,他的状态是“关闭”的,当业务失败时,当失败此时超过阈值,断路器开始工作,他的状态“打开”,但不会一直处于打开状态,每次调用都会先判断业务是否正常。Hystrix检查动态服务列表能否正常访问。
  • Eureka基于AP,访问频率达不到阈值60每秒,启动保护模式,不会动态维护列表。不正确,快速失效,回调配置断路器方法。
  • 方法自行处理,常用:①设置默认price②返回通用对象,json业务消费者经过层层封装调用后,访问业务提供者,他对外暴露RESTFul请求+json。业务提供者可以注入@Value属性值,但是这个值为了支持分布式环境的配置,引入配置中心,代替传统属性文件。要维护属性和属性的动态变化,配置中心就引入GIT,通过GIT来维护属性的值。

为什么引入配置中心:

微服务会管理很多服务器或集群,为了同时更新配置,还可以动态刷新最新的配置。

在实际大型项目中,如果并发量非常大,还需要对这个架构继续升级:

1)api网关可进行高可用
2)Eureka注册中心高可用
3)服务的提供者,采用负载均衡
4)Config-server配置中心高可用

GIT

进行通信,维护代码:
1、pull,每天一上班,把远程仓库的内容拉取到本地
2、add,可以一个代码文件写完,写完一段完整的业务,add
3、commit,上午提交,下班之前提交;一般来说add+commit
4、push,一个模块的代码全部完成,下班之前提交。
为什么每天下班之前要提交一次?集成测试,不同人写的代码在一起是否能运行。

GIT与SVN差异?

1、结构,SVN中央集权结构,GIT分散,分布式。
2、管理侧重点不同,SVN侧重管理相关文档,GIT侧重管理代码。
3、权限管理,SVN就可以根据配置的权限,限定用户的浏览下载权限,文档或者代码就非常安全。GIT目标:共享,在权限管理上非常弱。

配置中心

1、传统属性文件,当服务多了时,要修改属性时,很难同时修改大量机器上的属性文件,在短时间内属性值不同步。
配置中心单独服务器,单独集群
2、属性不能动态更新,需要重启服务器。
-注解@RefreshScope,支持手动刷新,bootstrap.yml/properties(它比@Value注解先执行,比application.yml/properties先执行。)关闭安全配置false,执行手动POST的刷新情况。
-SpringCloud-BUS总线,基于RabbitMQ。异步+MQ+并发,发布订阅。

二、MyBatisPlus

今日任务:
1)整体技术体系,实现user表的CURD操作,所有技术一起使用
2)新的技术,通用Mapper,实现单表CRED操作SQL不用谢,代替工具,升级版本MybatisPlus

MyBatisPlus

使用SpringBoot + Mybatis + MybatisPlus(MP) 全新注解方式,自动产生SQL语句,替代旧的通用Mapper。旧的通用Mapper是基于Mybatis拦截器的机制,而新的MybatisPlus是基于注解扫描机制,在启动服务时就进行加载,所以性能比旧的通用Mapper方式高很多。
在这里插入图片描述

三、Provider-User-Demo

1、画图
在这里插入图片描述
2、准备数据库
在这里插入图片描述
3、新建项目组
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
拷贝Zuul和EurekaServer
在这里插入图片描述
3、新建提供者项目ProviderUser
在这里插入图片描述
4、pom依赖

<!-- 父依赖 -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.4.RELEASE</version>
		<relativePath />
	</parent>
	<!-- 设定jdk版本,字符编码,SpringCloud版本地名 -->
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Dalston.SR1</spring-cloud.version>
	</properties>
	<dependencies>
		<!-- eureka配置中心 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<!-- web服务 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!-- mybatisplus/mybatis/jdbc/druid -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- mybatisplus与springboot整合 -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatisplus-spring-boot-starter</artifactId>
			<version>1.0.5</version>
		</dependency>
		<!-- MP 核心库 -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus</artifactId>
			<version>2.1.8</version>
		</dependency>
		<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper-spring-boot-starter</artifactId>
			<version>1.2.2</version>
		</dependency>
		<!-- alibaba的druid数据库连接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.0</version>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
</project>

5、POJO对象
要注意数据库表创建时主键id是自增的。
省略getter、setter方法。
在这里插入图片描述

//表和类进行映射
@TableName("user")
public class User implements Serializable{
//	id name birthday address
//	配置主键,主键自增策略
	@TableId(type=IdType.AUTO)
	private Integer id;
//	映射,全局配置驼峰规则,MybatisPlus自动修改,不需要再改为user_Name或userName
	private String name;
	@DateTimeFormat(pattern="yyyy-MM-dd")
	private Date birthday;
	private String address;

6、mapper 错误原因:没有写User泛型

package com.zz.mapper;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.zz.pojo.User;
public interface UserMapper extends BaseMapper<User>{

}

7、service

public interface UserService {

	public List<User> findAll();

}
@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private UserMapper userMapper;

//	查看用户列表	
	public List<User> findAll() {
		return userMapper.selectList(null);
	}
	
}

8、controller

@RestController
@RequestMapping("/user")
public class UserController {

//	我要显示所有的用户  是跟service做联系的
	@Autowired
	private UserService userService;
	
	@RequestMapping("/findAll")
	public List<User> findAll() {
		return userService.findAll();
	}
}

9、创建启动类
在这里插入图片描述

@SpringBootApplication
@MapperScan("com.zz.mapper")//mapper扫描包路径
@EnableEurekaClient
public class RunAppProviderUser {
	public static void main(String[] args) {
		SpringApplication.run(RunAppProviderUser.class, args);
	}
}

在这里插入图片描述
10、配置yml文件

server:
  port: 7900
  
spring:
  application:
    name: Provider-User
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mybatisdb
    username: root
    password: 
            
eureka:
  client:
    serviceUrl:
      defaultZone: http://user:password123@localhost:8761/eureka

mybatis:
  mapUnderscoreToCamelCase: true
  typeAliasesPackage: cn.zz.pojo
  mapperLocations: classpath:mappers/*.xml
  
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    
ribbon:
  eureka:  
    enabled: true
    
logging:
  level: 
    cn.jt.mapper: debug    

运行~

在这里插入图片描述

太丑了,Chrome中安装jsonview插件,遇到问题

在这里插入图片描述
将crx后缀替换为.rar
将rar解压,将“_metadata”改名为"metadata"

在这里插入图片描述
将解压的文件拖拽上去,安装完成啦
在这里插入图片描述
在这里插入图片描述

剩余步骤,添加、修改和删除

1、mapper不用动,因为继承了BaseMapper
2、Service、ServiceImpl

//	新增
	public Integer insert(User entity);
	
//	修改
	public Integer update(User entity);
	 
//	批量删除    mybatisplus自带批量功能
	/**
     * <p>
     * 删除(根据ID 批量删除)
     * </p>
     *
     * @param idList 主键ID列表
     * @return int
     */
    public void delete(List<Integer> ids);
// 新增
	public Integer insert(User entity) {
		// TODO Auto-generated method stub
		return userMapper.insert(entity);
		
	}

	// 修改
	public Integer update(User entity) {
		// TODO Auto-generated method stub
		return userMapper.updateById(entity);
		
	}
	
	// 批量删除
	public void delete(List<Integer> ids) {
		// TODO Auto-generated method stub
		userMapper.deleteBatchIds(ids);
	}

3、Controller

//	新增
	@RequestMapping("/insert/{name}/{birthday}/{address}")
	public String insert(User user) {
		try {
			int row = userService.insert(user);
		return "insert item:"+row+"~";
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			return "insert failed";
		}
	}
	
//	修改
	@RequestMapping("/update/{id}/{name}/{birthdya}/{address}")
	public String update(User user) {
		try {
			int row = userService.update(user);
			return "update item:"+row+"~";
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			return "update failed";
		}
		
		
	}
	
//	批量删除
	@RequestMapping("/delete/{id}")
	public String delete(@PathVariable Integer id) {
		
		try {
			List<Integer> ids = new ArrayList<Integer>();
			ids.add(id);
			userService.delete(ids);
			return "delete success";
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			return "delete failed";
		}
	}

来试一下吧~

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

模糊查询:

1、service、serviceImpl

//    按条件查询
    public List<User> findUser(User user);
//	按姓名进行条件查询
	public List<User> findUser(User user) {
//		封装where条件
		EntityWrapper wrapper = new EntityWrapper();
//		封装对象里面写的都是数据库的字段名称
		wrapper.like("name", user.getName());//QBC面向对象 
		return userMapper.selectList(wrapper);
	}

2、controller

	// 条件查询
	@RequestMapping("/find/{name}")
	public List<User> find(User user) {
		return userService.findUser(user);
	}

3、运行一下吧~
在这里插入图片描述
在这里插入图片描述

提供者配置完成!

四、Provider-Client-Demo

实现服务消费者

1)调用服务提供者,ribbon,feign接口(他接口写法和springmvc controller不一致,有很多的坑)
2)最著名的坑,日期。birthday不能直接转换,springmvc
json转换工具类objectMapper,对本地日期有时差(8小时),算完后,写一个拦截器,去完成增加8小时。Feign接参时,,全用字符串。
Http协议中没有数据类型,String,request.getParamter(“name”);
3)Feign不支持多级的@Requestmapping
4)Feign不能直接接受对象,很多参数post+ajax,参数以json提交。
5)加入Hystrix,和Zuul集成,在Zuul来具体调用断路器方法。

1、pom文件配置

<!-- 父依赖 -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.4.RELEASE</version>
		<relativePath />
	</parent>

	<!-- 过滤字符编码 设定java版本 制定SpringCloud版本地名 -->
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Dalston.SR1</spring-cloud.version>
	</properties>
	<dependencies>
		<!-- eureka注册中心依赖 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<!-- Hystrix熔断器 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>
		<!-- RESTFul -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-feign</artifactId>
		</dependency>
		<!-- web场景启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<!-- SpringCloud项目管理依赖 -->
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

2、UserFeign和POJO-User
新建UserFeign,把Provider中的Controller的方法复制过来,发现需要User对象,所以把POJO类拷过来,去掉其与MybatisPlus相关联的注解。

在这里插入图片描述
在这里插入图片描述

此处的更改,在Consumer,所以对应的provider也要保持同步!!!

在方法上方的注解有一个常用的@ResponseBody,现在有在方法传参位置使用的@RequestBody【未使用】

Feign:

@FeignClient("Provider-User")
public interface UserFeign {
	@RequestMapping("/user/findAll")	//必须路径写全
	public List<User> findAll();		//POJO中的日期的处理
	
	@RequestMapping("/user/insert/{name}/{birthday}/{address}")		//对象传参,内部走的是json提交
	public String insert(@PathVariable("name") String name,
			@PathVariable("birthday")  String birthday,
			@PathVariable("address") String address);
	
	@RequestMapping("/user/update/{id}/{name}/{birthday}/{address}")
	public String update(
			@PathVariable("id") Integer id,
			@PathVariable("name") String name, 
			@PathVariable("birthday") String birthday,
			@PathVariable("address") String address);
	
	@RequestMapping("/user/delete/{id}")
	public String delete(@PathVariable("id") Integer id);
	
	
	@RequestMapping("/user/find/{name}")
	public List<User> find(@PathVariable("name") String name);

4、Controller

@RestController
@RequestMapping("/user")
public class UserController {
	@Autowired
	private UserFeign userFeign;
	
//	查找全部
	@RequestMapping("/findAll")	//必须路径写全
	public List<User> findAll(){
		return userFeign.findAll();
	}
	
//	模糊查找
	@RequestMapping("/find/{name}")
	public List<User> find(User user) {
		return userFeign.find(user.getName());
	}
	
//	插入
	@RequestMapping("/insert/{name}/{birthday}/{address}")
	public String insert(User user){
		return userFeign.insert(user.getName(), user.getBirthday(), user.getAddress());
	}
	
//	更改
	@RequestMapping("/update/{id}/{name}/{birthday}/{address}")
	public String update(User user) {
		return userFeign.update(user.getId(),user.getName(),user.getBirthday(),user.getAddress());
	}
	
	//按id批量删除
	@RequestMapping("/delete/{id}")
	public String delete(@PathVariable Integer id){
		return userFeign.delete(id);
	}
}

5、配置yaml文件

server:
  port: 9001
spring:
  application:
    name: ConsumerUser
eureka:
  client:
    serviceUrl:
      defaultZone: http://user:password123@localhost:8761/eureka
logging:
  level:
    root: INFO

6、配置启动类

@SpringCloudApplication//使用SpringCloud管理项目架构
@EnableFeignClients//支持Feign的Client用户端
@EnableCircuitBreaker//启动Hystrix
public class RunAppConsumerUser {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SpringApplication.run(RunAppConsumerUser.class, args);
	}
}

配置完成!

首先要启动Eureka,然后启动服务端,最后启动消费者测试一下吧!

错点:为什么服务提供者没有在Eureka注册中心显示?因为ProviderUser的启动类中没有添加注解@EnableEurekaClient!!!

在这里插入图片描述
在这里插入图片描述
使用访问Client端实现:查找全部。

发现问题:日期显示不正确。

在这里插入图片描述

顺便也把provider-user的pojo也改了。对应起来

在这里插入图片描述
引入Ribbon进行负载均衡配置:
1)直接调动RestTemplate.getForObject():
2)@Bean@LoadBlance
3)Ribbon客户端,实现负载均衡配置
在提供者application.yml配置下面语句,启用Ribbon:
ribbon:
eureka:
enabled: true

模糊查找:
在这里插入图片描述
添加:
在这里插入图片描述
修改:
在这里插入图片描述
删除:
在这里插入图片描述

总结错误原因:

provider和consumer对应的方法的传参、注解 都要保持一致!!!

provider和consumer对应的方法的传参、注解 都要保持一致!!!

provider和consumer对应的方法的传参、注解 都要保持一致!!!

配置检验网关
1、pom
2、yaml

server:
  port: 8050
spring:
  application:
    name: gateway-zuul
eureka:
   client:
    serviceUrl:
      defaultZone: http://user:password123@localhost:8761/eureka
zuul:
  ignoredServices: '*'
  routes:
    app-provider-user:            
      path: /user/**
      serviceId: Provider-User

3、FallBack

//未被实例化,可通过包扫描加载。但是既不属于Controller,也不属于Service 所以使用@Component
@Component
public class ZullFallBack implements ZuulFallbackProvider {

//	获取路由 application.name-->Provider-User :provider-user ,同yaml文件配置。
	public String getRoute() {
		// TODO Auto-generated method stub
		return "Provider-User";
	}

//	设置返回值,通常用Json体现,utf-8防止中文乱码
	public ClientHttpResponse fallbackResponse() {
		// TODO Auto-generated method stub
		return new ClientHttpResponse() {//匿名内部类

			
//			请求响应头信息        contentType和字符类型
			public HttpHeaders getHeaders() {
				// 返回类型为Json,设置字符集为UTF-8
				HttpHeaders headers = new HttpHeaders();
				headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
				return headers;
			}


//			响应体,具体返回内容,就是Hystrix断路器设定,断路时显示的默认内容
			public InputStream getBody() throws IOException {
				String defaultValue = "Default Value From Zuul!";//标准应为Json字符串
				return new ByteArrayInputStream(defaultValue.getBytes());
			}
			
//			返回文字描述====固定内容 
			public String getStatusText() throws IOException {
				// TODO Auto-generated method stub
				return HttpStatus.BAD_REQUEST.getReasonPhrase();
			}
			
//			返回状态码
			public HttpStatus getStatusCode() throws IOException {
				// TODO Auto-generated method stub
				return HttpStatus.BAD_REQUEST;
			}

//			返回二进制状态码
			public int getRawStatusCode() throws IOException {
				// 可不写
				return 0;
			}
//			关闭释放资源
			public void close() {
				// 可不写
			}
		};
	}

}

4、启动类

@SpringCloudApplication    //项目架构
@EnableZuulProxy		//标志API网关
@EnableEurekaClient		//Eureka客户端
public class RunApplicationZuul {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SpringApplication.run(RunApplicationZuul.class, args);
	}
}

运行:
1、启动Eureka
2、启动提供者1和2
3、启动消费者
4、启动zuul网关配置
访问zuul:localhost:8050/user/user/***
在这里插入图片描述
在这里插入图片描述
网关、负载均衡配置成功。负载均衡Ribbon是配置在两个提供者yaml上的。
在这里插入图片描述

加了Zuul有了什么变化?

1、fallback,写在zuul
2、如果直接访问消费者,Feign @RequestBody,httpclientPost请求就可以
3、如果访问Zuul,调用消费者Feign @RequestBody,httpclientJson请求

注意问题:断路器没生效

①可能是因为 没有在Consumer的入口类中添加@EnableCircuitBreaker//启动Hystrix,支持Hystrix断容器,导致访问zuul停掉其中一个provider时候出现显示异常。
②Zuul自带断路器。它的FallBack实现ZuulFallbackProvider接口,获取路由是提供者的服务名称。
在这里插入图片描述

总结:

一、
1、Eureka+Provider1+Provider2+Consumer(Feign+Hystrix+Ribbon)+Zull(FallBack)
2、Eureka没有变化,直接使用【版本号不同】。
3、User1,对象传参,REST日期(消费者和提供者,日期类型字符串,保持一致),对象传参,Feign支持不好,改成json传参,变相对象传参@RequestBody【不会用暂时】
4、User1和User2,支持一个业务多个通道,提高并发,必须有区别:端口号不同。
application.name必须重复,Eureka会按名称分到一组,Ribbon按组来轮询。
5、Ribbon负载均衡配置在Provider的yaml上,通过Consumer来调用或者Zuul来调用。如果对象的传参Feign 中引用@ResponseBody。如果直接访问消费者,以post请求,不能get请求。如果通过zuul访问,必须以json方式。
6、Zuul,要修改pom文件,设定断路器的“fallback”的路由【provider的application-name】。
二、
1、服务框架Zuul直接调用提供者
2、Zuul+Consumer消费者,经过FeignRESTFul支持,Ribbon负载均衡
3、拦截器,影响性能。Feign不够了解,掌握一般用法。
4、全部整合起来,消费者调用Feign,Feign支持不够好,问题很多:
1)@FeignClien(“Provider-User”) 调用的是服务提供者
2)接口中方法,代表提供者,直接仿照提供者,高度一致
3)Controller支持@RequestMapping多级。但是Feign不支持多级的@RequestMapping
4)Feign中的@PathVariable的映射名每一个都要写
5)对象参数SpringCloud(RESTFul+Json)支持json传参,SpringCloud内部根据@RequestBody,把获取json强制转换为user java对象
6)请求方式如果直接调用消费者,使用htpclient模拟post请求;如果从zuul调用,一HttpClient模拟json请求。设置ContentType :son/utf-8
5、在zuul中单独配置映射

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
电子图书资源服务系统是一款基于 Java Swing 的 C-S 应用,旨在提供电子图书资源一站式服务,可从系统提供的图书资源中直接检索资源并进行下载。.zip优质项目,资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目。 本人系统开发经验充足,有任何使用问题欢迎随时与我联系,我会及时为你解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(若有),项目具体内容可查看下方的资源详情。 【附带帮助】: 若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步。 【本人专注计算机领域】: 有任何使用问题欢迎随时与我联系,我会及时解答,第一时间为你提供帮助,CSDN博客端可私信,为你解惑,欢迎交流。 【适合场景】: 相关项目设计中,皆可应用在项目开发、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面中 可借鉴此优质项目实现复刻,也可以基于此项目进行扩展来开发出更多功能 【无积分此资源可联系获取】 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。积分/付费仅作为资源整理辛苦费用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值