SpringBoot--微服务

微服务阶段

javase:OOP

mysql:持久化

html+css+jquery+框架:视图,框架,css

javaweb:独立开发MVC三层架构的安慰你观战了:原始

ssm:框架:简化了我们的开发流程,配置也开始较为复杂

war:tomcat运行

spring再简化:SpringBoot-jar,内嵌Tomcat

来来来,给自己一个规划
在这里插入图片描述

SpringBoot:快速入门
什么是Spring

在这里插入图片描述

Spring是如何简化Java开发的

在这里插入图片描述

什么是SpringBoot

在这里插入图片描述
约定大于配置!!!约定大于配置!!!约定大于配置!!!

Spring Boot的主要优点:
在这里插入图片描述
给自己定目标是:

程序=数据结构 + 算法=>程序员

不要关注的是:

程序=面向对象 + 框架=>码农

微服务
什么是微服务?

在这里插入图片描述

单体应用架构

在这里插入图片描述

微服务架构

在这里插入图片描述
官方说明

如何构建微服务

在这里插入图片描述

第一个SpringBoot程序

到底多么简单:

  • jdk1.8
  • maven 3.6.1
  • springBoot最新版
  • IDEA

官方:提供了一个快速生成的网站!IDEA继承了这个网站

  • 可以在官网直接下载后,导入idea开发,(快速开始里面)

  • 在这里插入图片描述

  • 直接使用idea创建一个springboot项目(一般开发直接在idea中创建)

添加依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

原理初探

自动配置:

pom.xml
  • spring-boot-dependencies:核心依赖在父工程中!
  • 我们在写或者引入一些Springboot依赖的时候,不需要指定版本,就因为有这些版本仓库

启动器

  • <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    
  • 启动器:说白了就是Springboot的启动场景

  • 比如spring-boot-starter-web,他就会帮我们自动导入web环境所有的依赖!

  • springboot会将所有的功能场景,都变成一个一个的启动器

  • 我们要使用什么功能,就只需要找到对应的启动器就可以了 starter

主程序
//@SpringBootApplication:标注这个类是一个springboot的应用
@SpringBootApplication
public class Spingboot01Application {

    public static void main(String[] args) {
        
        //将springboot应用启动
        SpringApplication.run(Spingboot01Application.class, args);
    }

}

  • 注释

    • @SpringBootConfiguration  : springboot的配置
          @Documented:spring的配置类
      	@Configuration:说明这也是一个spring的组件
          
      @EnableAutoConfiguration:自动配置
          @AutoConfigurationPackage:自动配置包
          			     @Import(AutoConfigurationPackages.Registrar.class):导入选择器 包注册
        @Import(AutoConfigurationImportSelector.class):自动导入选择器
        
        
      //获取所有的配置
      List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
      

获取候选的配置

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}

META-INF/spring-factories:自动配置的核心文件
在这里插入图片描述
结论:springboot所有自动配置都是在启动的时候扫描并加载:spring,factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是非成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功!
在这里插入图片描述

Run

在这里插入图片描述

SpringApplication

在这里插入图片描述

spingboot的配置
YAML

在这里插入图片描述

YAML语法
#k=v
#对空格的要求十分高
#普通的key-value
name: ljh

#对象
student:
  name: ljh
  age: 3

#行内写法
student: {name: ljh,age: 3}

#数组
pets:
  - cat
  - dog
  - pig

pets: [cat,dog,pig]

对比properties

#properties只能保存键值对
name=ljh
student.name=ljh
student.age=3
yaml可以直接给实体类赋值

在这里插入图片描述
点开提示会有官方叫你加依赖,那就加吧!

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

在这里插入图片描述
加了不爆红了
在这里插入图片描述
上代码:

Person.java

package com.ljh.pojo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> map;
    private List<Object> lists;
    private Dog dog;

    public Person() {
    }

    public Person(String name, Integer age, Boolean happy, Date birth, Map<String, Object> map, List<Object> lists, Dog dog) {
        this.name = name;
        this.age = age;
        this.happy = happy;
        this.birth = birth;
        this.map = map;
        this.lists = lists;
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Boolean getHappy() {
        return happy;
    }

    public void setHappy(Boolean happy) {
        this.happy = happy;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public List<Object> getLists() {
        return lists;
    }

    public void setLists(List<Object> lists) {
        this.lists = lists;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", happy=" + happy +
                ", birth=" + birth +
                ", map=" + map +
                ", lists=" + lists +
                ", dog=" + dog +
                '}';
    }
}

Dog.java

package com.ljh.pojo;

import org.springframework.stereotype.Component;

@Component
public class Dog {
    private String name;
    private Integer age;

    public Dog() {
    }

    public Dog(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试:

package com.ljh;

import com.ljh.pojo.Dog;
import com.ljh.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Springboot02ConfigApplicationTests {
    @Autowired
    Person person = new Person();
    @Test
    void contextLoads() {
        System.out.println(person);
    }

}

配置文件:application.yaml

sever:
  port: 8087

Person:
  name: ljh
  age: 3
  happy: false
  birth: 2019/11/02
  map: {k1: v1,k2: v2}
  list:
    - code
    - music
    - girl
  dog:
    name: 旺财
    age: 3

输出结果:

Person{name=‘ljh’, age=3, happy=false, birth=Sat Nov 02 00:00:00 CST 2019, map={k1=v1, k2=v2}, lists=null, dog=Dog{name=‘旺财’, age=3}}

yaml的灵活性

sever:
  port: 8087

Person:
  name: ljh${random.uuid}
  age: ${random.int}
  happy: false
  birth: 2019/11/02
  map: {k1: v1,k2: v2}
  list:
    - code
    - music
    - girl
  dog:
    #存在就person.hello,不存在就hello
    name: ${person.hello:hello}_旺财
    age: 3
唠两句

properties的配置解决乱码,先把它配了再说
在这里插入图片描述
@Value是针对properties配置的,可以发现yaml强大多了,propertis使用起来并不友好,需要为每个单独注解赋值,比较麻烦。
在这里插入图片描述

//javaConfig绑定我们配置文件的值,可以采取这种方式
//加载指定的配置文件
//@PropertySource("classpath:ljh.properties")
public class Person {

松散绑定:就是这样

dog:
  first-name: 大黄
  age: 3
@Component
@ConfigurationProperties(prefix = "dog")
public class Dog {
    private String firstName;
    private Integer age;

输出结果:
在这里插入图片描述
如果手动改,记得getter和setter方法名一起改哟,不然不会生效的

JSR303数据校验

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

@Component
@ConfigurationProperties(prefix = "person")
//javaConfig绑定我们配置文件的值,可以采取这种方式
//加载指定的配置文件
//@PropertySource("classpath:ljh.properties")
@Validated//数据校验
public class Person {
    @Email(message = "邮箱格式错误")
   
多环境配置及配置文件位置

在这里插入图片描述
四个位置:优先级
在这里插入图片描述

优先级如图,默认是最低的,可以写不一样的端口号来测

多环境切换

在这里插入图片描述

注意,前面都用application-xx

看yaml的写法,明显优势,多块文档
在这里插入图片描述

自动配置原理再理解

在这里插入图片描述

#配置文件到底能写什么?---联系--和spring.factories
#在我们这配置文件中能配置的东西,都存在一个固有的规律
#xxxAutoConfiguration:默认值  xxxProperties和配置文件绑定,我们就可以使用自定义的配置了
#通过debug: true来查看,哪些自动配置类,哪些没有生效
debug: true

实例:
在这里插入图片描述

随便写一个,ctrl点击,broker-url进入:
在这里插入图片描述

然后我们定位去找jar包下面的类,或者ctrl点类名
在这里插入图片描述

第一个就是自动装配类

或者这样去找,避免找错
在这里插入图片描述

发现有红色,没生效,需要添加依赖,(怎么加,找对应的启动器,加载pom文件中),
在这里插入图片描述

果然,看debug: true打印的日志能发现没生效,因为缺少依赖

@Conditional

在这里插入图片描述

SpringBoot Web开发

jar: webapp!

自动装配

springboot到底帮我们配置了什么?我们能不能进行修改?能修改哪些东西?能不能扩展?

  • xxxAutoConfiguration向容器中自动配置组件
  • xxxProperties:自动配置类,装配配置文件中自定义的一些内容!

要解决的问题:

  • 导入静态资源
  • 首页
  • jsp,模板引擎Thymeleaf
  • 装配扩展SpringMVC
  • 增删改查
  • 拦截器
  • 国际化
静态资源
		@Override
		public void addResourceHandlers(ResourceHandlerRegistry registry) {
            //先判断在application.properties中是否配置
			if (!this.resourceProperties.isAddMappings()) {
				logger.debug("Default resource handling disabled");
				return;
			}
			addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
			addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
				registration.addResourceLocations(this.resourceProperties.getStaticLocations());
				if (this.servletContext != null) {
					ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
					registration.addResourceLocations(resource);
				}
			});
		}

什么是webjars?
在这里插入图片描述

直接把jquery的maven放入pom.xml
在这里插入图片描述

第一种方式
在这里插入图片描述

第二种方式
在这里插入图片描述

我们可以增加
在这里插入图片描述

可以在文件夹中放入文件测试
在这里插入图片描述

自定义配置静态资源路径
在这里插入图片描述

首页

默认index.html
在这里插入图片描述

模板引擎

在这里插入图片描述

thymeleaf语法

在这里插入图片描述

MVC配置原理
修改SpringBoot的默认配置
方式一

在这里插入图片描述

package com.ljh.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Locale;

//如果,你向diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配
//扩展springmvc dispatchservlet
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    //ViewResolver实现了视图解析器接口的类,我们就可以把他看做视图解析器
    @Bean
    public ViewResolver myviewResolver(){
        return new MyviewResolver();
    }

    public static class MyviewResolver implements ViewResolver{

        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            return null;
        }
    }
}


扩展使用SpringMVC

在这里插入图片描述

package com.ljh.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class MyMvcConfig1 implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/ljh").setViewName("test");
    }
}


运行结果:
在这里插入图片描述

注意:
在这里插入图片描述

看到没,不要加@EnableWebMvc?是因为@Configuration里面
在这里插入图片描述

但是@EnableWebMvc里面继承了画红线这个类,炸裂

这告诉我们要看源码哟

页面国际化

在这里插入图片描述

上代码:
在这里插入图片描述

package com.ljh.config;

import org.springframework.web.servlet.LocaleResolver;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

public class MyLocaleResolver implements LocaleResolver {
    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        //获取请求中的语言参数
        String language = request.getParameter("1");
        Locale locale = Locale.getDefault();
        //如果请求的链接携带了国际化的参数
        if(!StringUtils.isEmpty(language)){
            //zh_CN
            String[] split = language.split("_");
            //国家,地区
            locale =  new Locale(split[0],split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

在这里插入图片描述

  1. 404
    在这里插入图片描述
    在这里插入图片描述
Data
简介

在这里插入图片描述

上代码:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
    password: 123456
    username: root
    driver-class-name: com.mysql.jdbc.Driver
package com.ljh.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
public class JdbcController {
    @Autowired
    JdbcTemplate jdbcTemplate;

    //查询数据库的所有信息
    //没有实体类,数据库中的东西,怎么获取?
    @RequestMapping("/userList")
    public List<Map<String,Object>> userList(){
        String sql = "select * from user";
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
        return list;
    }
    @RequestMapping("/addUser")
    public String addUser(){
        String sql = "insert into user(id,name,pwd) values (10,'kk','1234')";
        jdbcTemplate.update(sql);
        return "update-ok";
    }
    @RequestMapping("/updateUser/{id}")
    public String updateUser(@PathVariable("id")int id){
        String sql = "update  user set name=?,pwd=? where id="+id;
        //封装
        Object[] o = new Object[2];
        o[0] = "小明";
        o[1] = "123456";

        jdbcTemplate.update(sql,o);
        return "update-ok";
    }
    @RequestMapping("/deleteUser/{id}")
    public String deleteUser(@PathVariable("id")int id){
        String sql = "delete from user where id="+id;


        jdbcTemplate.update(sql,id);
        return "update-ok";
    }
}

在这里插入图片描述

上代码:
在这里插入图片描述

 <!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
package com.ljh.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import javax.sql.DataSource;
import java.util.HashMap;

@Configuration
public class DuridConfig {
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource duridDataSource(){
        return new DruidDataSource();
    }
    @Bean
    public ServletRegistrationBean a(){
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet());
        //后台需要有人登陆,账号密码配置
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("loginUsername","root");//登录的key是固定的loginUsername和loginPassword
        hashMap.put("loginPassword","123456");
        //允许谁可以访问
        hashMap.put("allow","");
        bean.setInitParameters(hashMap);//设置初始化参数
        return bean;

    }
    //filter
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new WebStatFilter());
        //可以过滤哪些请求
        HashMap<String, String> hashMap = new HashMap<>();
        //这些东西不进行统计
        hashMap.put("exclusions","*.js,*.css,/druid/*");//登录的key是固定的loginUsername和loginPassword

        //允许谁可以访问
        hashMap.put("allow","");
        bean.setInitParameters(hashMap);//设置初始化参数
        return bean;
    }
}

Mybatis
整合包
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
<dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

application.yaml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
    password: 123456
    username: root
    driver-class-name: com.mysql.jdbc.Driver

#整合mybatis
mybatis:
  type-aliases-package: com.ljh.pojo
  mapper-locations: classpath:/mybatis/mapper/*.xml

代码:

User.java

package com.ljh.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;

}

UserMapper

package com.ljh.mapper;

import com.ljh.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;
//这个注解表示这是一个mybaatis的Mapper类:Dao
@Mapper
@Repository
public interface UserMapper {
    List<User> queryUserList();

    User queryUserById(int id);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--nameSpace绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.ljh.mapper.UserMapper">
    <select id="queryUserList" resultType="User">
    select * from user
  </select>

    <select id="queryUserById" parameterType="int" resultType="User">
    select * from user where id = #{id}
  </select>

</mapper>

controller

package com.ljh.controller;

import com.ljh.mapper.UserMapper;
import com.ljh.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @GetMapping("/queryUserList")
    public List<User> queryUserList(){
        List<User> list = userMapper.queryUserList();
        for (User user : list) {
            System.out.println(user);

        }
        return list;
    }

}

项目结构:
在这里插入图片描述

SpringSecurity(安全)

在web开发中,安全第一位!过滤器,拦截器

功能性需求:否

做网站:安全应该在什么时候考虑?设计之初!

  • 漏洞,隐私泄露
  • 架构一旦确定

shiro,SpringSecurity:很像~除了类不一样,名字不一样;

认证,授权(vip1,vip2,vip3)

  • 功能权限
  • 访问权限
  • 菜单权限
  • 拦截器,过滤器:大量的原生代码冗余

MVCSPRINGSPRINGBOOT–架构思想

AOP:横切配置类
在这里插入图片描述

第一步:pom.xml
在这里插入图片描述

第二步:写配置类:认证和授权
在这里插入图片描述

补充完整,后面接.roles("**")
在这里插入图片描述

开启注销,记住我功能
在这里插入图片描述

第三步:controller
在这里插入图片描述

Shiro

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

代码过程:

可以去github下载下来,模仿着创建项目

  1. 依赖
    在这里插入图片描述

  2. 配置文件shiro.ini(github里面拷贝)
    在这里插入图片描述

  3. quickStart
    在这里插入图片描述

SpringBoot整合shiro

在这里插入图片描述

实现登录拦截

在这里插入图片描述

shiro实现用户认证

在这里插入图片描述

shiro整合Mybatis

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

shiro请求授权实现

在这里插入图片描述

增加字段perms
在这里插入图片描述

shiro整合Thymleaf

在这里插入图片描述

Swagger

在这里插入图片描述

Swagger简介

在这里插入图片描述

SpringBoot集成Swagger

在这里插入图片描述

配置Swagger

Swagger的bean实例Docket
在这里插入图片描述

Swagger配置扫描接口

Docket.select()
在这里插入图片描述

配置是否启动swagger
在这里插入图片描述

我只希望我的Swagger在生产环境中使用,在发布的时候不使用?
在这里插入图片描述

配置API文档的分组
在这里插入图片描述

实体类配置:
在这里插入图片描述

controller
在这里插入图片描述

任务

在这里插入图片描述

邮箱:
在这里插入图片描述

定时任务:
在这里插入图片描述

SpringBoot整合

在这里插入图片描述

一步一步来

新建项目:
在这里插入图片描述

自定义RedisTemplate

在这里插入图片描述

重复了@Autowrided不是我们的自定义怎么办
在这里插入图片描述

分布式Dubbo+Zookeeper+springBoot

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

步骤分析
在这里插入图片描述

RPC两个核心模块:通讯、序列化
在这里插入图片描述

Dubbo概念

在这里插入图片描述

zookeeper就是注册组件

zookeeper下载地址
在这里插入图片描述
在这里插入图片描述

安装完成
在这里插入图片描述

服务注册发现实例

在这里插入图片描述

依赖:
在这里插入图片描述
在这里插入图片描述

消费者依赖:
在这里插入图片描述

扩展

在这里插入图片描述
POST、GET、@RequestBody和@RequestParam区别

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_42287451

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值