SpringBoot 第一篇

一、SpringBoot介绍

1.1 SpringBoot简介

 SpringBoot是由Pivotal团队研发的,SpringBoot并不是一门新技术,只是将之前常用的Spring,SpringMVC,data-jpa等常用的框架封装到了一起,帮助你隐藏这些框架的整合细节,实现敏捷开发。

SpringBoot是基于约定优先于配置的,主要作用就是用来简化Spring应用的初始搭建以及开发过程!

后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。

1.2 SpringBoot的特点

1.基于Spring的开发提供更快的入门体验

2.开箱即用,没有代码生成,也无需XML配置,同时也可以修改默认值来满足特定的需求。

3.提供了一些大型项目中常见的非功能性特性,外部配置等。

4.SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式

1.3 SpringBoot的核心依赖

1.起步依赖

起步依赖本质是一个Maven项目对象模型(Project Object Model,POM),定义了对其它库的传递依赖,这些东西加在一起即可支持某项功能。

简单的来说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

2.自动配置

SpringBoot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多的因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。

二、SpringBoot介绍

SpringBoot是由Pivotal团队研发的,SpringBoot并不是一门新技术,只是将之前常用的Spring,SpringMVC,data-jpa等常用的框架封装到了一起,帮助你隐藏这些框架的整合细节,实现敏捷开发。

SpringBoot就是一个工具集。

SpringBoot特点:

  • SpringBoot项目不需要模板化的配置。

  • SpringBoot中整合第三方框架时,只需要导入相应的starter依赖包,就自动整合了。

  • SpringBoot默认只有一个.properties的配置文件,不推荐使用xml,后期会采用.java的文件去编写配置信息。

  • SpringBoot工程在部署时,采用的是jar包的方式,内部自动依赖Tomcat容器,提供了多环境的配置。

  • 后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。

三、SpringBoot快速入门

网站创建地址:https://start.spring.io/

 

 

项目创建完成!

此时pom.xml文件中会自动导入springboot所需依赖,并且在src下会生成一个配置类。

注意:若pom.xml中依赖无法下载,需要修改maven工程对应的settings.xml文件,找到settings.xml文件中的镜像配置,原因是maven中央仓库下载不下来springboot关联的架包,所以建议使用阿里云的镜像.

<mirrors>
    <!-- mirror
     | Specifies a repository mirror site to use instead of a given repository. The     
repository that
     | this mirror serves has an ID that matches the mirrorOf element of this     mirror. 
IDs are used
     | for inheritance and direct lookup purposes, and must be unique     across the set of mirrors.
     |
    -->
      
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus-aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

</mirrors>

运行配置类,看到如下页面,表示启动成功!

配置自动编译

手动编写Controller进行进一步测试(注意:需要将controller类,放在启动类的子包中或者同级包下)

package com.qf.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @RequestMapping("/login")
    public String login(){

        System.out.println("登录");

        return "success";
    }
}

 重新启动配置类,访问:http://localhost:8080/login

四、SpringBoot热部署配置

为了方便开发,可以在创建项目时手动勾选热部署,或导入该依赖,就不需要每次重启配置类

<!--热部署配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

配置自动编译

 最后Shift+Ctrl+Alt+/,选择Registry(选完之后再次查看一下是否勾选上)

 再次重新运行一次配置类即可!

五、SpringBoot中的默认配置

可以从jar包中找到SpringBoot的默认配置文件位置

SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置,SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)文件。

其中,application.properties文件是键值对类型的文件,除此之外,SpringBoot还可以使用yml文件进行配置,YML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁,YML文件的扩展名可以使用.yml或者.yaml。

application.properties方式修改默认配置

 application.yml方式修改默认配置:(注意:yml文件中空格表示层级关系)

yml文件支持的配置

#普通数据的配置
name: jack

#对象的配置
user:
  username: rose
  password: 123

#配置数组
array:
    beijing,
    tianjin,
    shanghai

#配置集合
yangl:
  test:
    name: tom
    arr: 1,jack,2,tom  
    list1:      #这种对象形式的,只能单独写一个对象去接收,所以无法使用@value注解获取
      - zhangsan
      - lisi
    list2:
      - driver: mysql
        port: 3306
      - driver: oracle
        port: 1521
    map:
      key1: value1
      key2: value2

#端口配置
server:
  port: 8081

把yml文件中配置的内容注入到成员变量中

第一种,创建UserController,通过使用@Value注解方式注入

package com.qf.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;

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

    @Value("${name}")
    private String name;

    @Value("${user.username}")
    private String username;

    @Value("${user.password}")
    private String password;

    @Value("${arr}")
    private String[] arr;

    @RequestMapping("test")
    public String test(){
        System.out.println("@value: ......");
        System.out.println(name);
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(arr));

        return "success";
    }
}

运行配置类,输入url:http://localhost:8080/user/test

 控制台输出:

第二种方式,使用ConfigurationProperties注解的方式,提供get/set方法

创建YmlController 

package com.qf.controller;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@RequestMapping("yml")
@ConfigurationProperties(prefix = "zhangs.test")
public class YmlController {

    private String name;
    private String[] arr;
    private List<String> list1;
    private List<Map<String,Object>> list2;
    private Map<String,Object> map;

    @RequestMapping("test")
    public String test(){
        System.out.println("@ConfigurationProperties: .....");
        System.out.println(name);
        System.out.println(Arrays.toString(arr));
        System.out.println(list1);
        System.out.println(list2);
        System.out.println(map);
        return "success";
    }

    public String getName() {
        return name;
    }

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

    public String[] getArr() {
        return arr;
    }

    public void setArr(String[] arr) {
        this.arr = arr;
    }

    public List<String> getList1() {
        return list1;
    }

    public void setList1(List<String> list1) {
        this.list1 = list1;
    }

    public List<Map<String, Object>> getList2() {
        return list2;
    }

    public void setList2(List<Map<String, Object>> list2) {
        this.list2 = list2;
    }

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

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

启动配置类,在浏览器中输入对应的url: http://localhost:8080/yml/test

如果使用@ConfigurationProperties注解时提示以下信息

 导入以下依赖即可(也可以不导入)

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

六、SpringBoot中的异常处理

6.1 创建ExceptionController测试类

package com.qf.controller;

import com.qf.exception.MyException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ExceptionController {

    @RequestMapping("/exception")
    public String exception(){

        int i = 1/0;

        return "exception";
    }

    @RequestMapping("/myexception")
    public String myexception()throws MyException{

        throw new MyException("自定义异常");

    }
}

6.2 创建自定义异常类

package com.qf.exception;

public class MyException extends Exception{

    public MyException(String msg){
        super(msg);
    }
}

6.3 创建MyExceptionHandler全局异常处理类

package com.qf.exception;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;

//拦截异常
@RestControllerAdvice
public class MyExceptionHandler {

    //处理指定异常
    @ExceptionHandler(value = Exception.class)
    public Object Handler1(Exception e, HttpServletRequest request){

        System.out.println("Handler1");

        HashMap<String, Object> map = new HashMap<>();

        map.put("msg",e.getMessage());
        map.put("url",request.getRequestURL());

        return map;
    }

    @ExceptionHandler(value = MyException.class)
    public Object Handler2(MyException e, HttpServletRequest request){

        System.out.println("Handler2");

        HashMap<String, Object> map = new HashMap<>();

        map.put("msg",e.getMessage());
        map.put("url",request.getRequestURL());

        return map;
    }
}

访问:http://localhost:8080/exception 以及 http://localhost:8080/myexception测试

七、SpringBoot中的过滤器(Listener操作同理)

7.1 创建过滤器

package com.qf.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(urlPatterns = "/filter/*")//指定拦截路径
public class LoginFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("LoginFilter ");

        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse)response;

        chain.doFilter(req,resp);
    }
}

7.2 创建Controller测试类

package com.qf.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/filter")
public class FilterController {

    @RequestMapping("/login")
    public String login() {

       System.out.println("登录");

        return "login";
    }
}

7.3 在启动类添加@ServletComponentScan注解

package com.qf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan//Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册
public class Springboot02Application {

    public static void main(String[] args) {
        SpringApplication.run(Springboot02Application.class, args);
    }

}

访问controller测试即可

八、SpringBoot中的拦截器

8.1.创建自定义拦截器

package com.qf.interceptor;

import org.omg.PortableInterceptor.Interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class MyInterceptor implements HandlerInterceptor {

    
    //进入controller方法之前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;//true表示放行,false表示不放行
    }

    //调用完controller之后,视图渲染之前
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    //页面跳转之后,整个流程执行之后,一般用于资源清理操作
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}

8.2创建拦截器配置类

package com.qf.interceptor;

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

@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //设置拦截器并指定拦截路径
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/interceptor/*");
        //registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");//拦截所有
        //registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/test");//指定不拦截
        //添加自定义拦截器
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

8.3在static目录下创建index.html以及controller测试类

package com.qf.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class InterceptorController {

        @RequestMapping("/interceptor/myinterceptor")
        public String myinterceptor(){

            System.out.println("myinterceptor");

            return "/index.html";
        }
}

访问controller测试即可

九、SpringBoot整合MyBatis

9.1 导入依赖

<!--  引入mybatis相关依赖,必须写版本号 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

<!--  引入mysql相关依赖,如果不写版本号,引入的8.0以上版本
 可以设置为其他版本
 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

9.2 在application.properties中配置数据库信息

# 数据库配置
# 默认使用mysql的驱动是8.x的版本,注意driver-class-name,url中增加时区的配置
spring.datasource.url=jdbc:mysql://localhost:3306/java1909?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

9.3使用之前的Account表,创建实体类

package com.qf.pojo;

public class Account {
    
    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

9.4 创建mapper

package com.qf.mapper;

import com.qf.pojo.Account;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface AccountMapper {

    public List<Account> findAll();
}

9.5在src\main\resources\mapper路径下创建对应的AccountMapper.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" >

<mapper namespace="com.qf.mapper.AccountMapper">

    <select id="findAll" resultType="com.qf.pojo.Account">
        select * from account
    </select>

</mapper>

9.6在application.properties中添加mybatis的信息

# mybatis配置
# 配置别名需要扫描的包
mybatis.type-aliases-package=com.qf.pojo
# 引入映射文件
mybatis.mapper-locations=classpath:mapper/*.xml
# 配置日志在控制台显示sql语句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

9.7创建service

package com.qf.service;

import com.qf.pojo.Account;

import java.util.List;

public interface AccountService {

    public List<Account> findAll();
}

9.8创建serviceImpl

package com.qf.service.impl;

import com.qf.mapper.AccountMapper;
import com.qf.pojo.Account;
import com.qf.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class AccountServiceImpl implements AccountService {
    
    @Autowired
    private AccountMapper accountMapper;
    
    @Override
    public List<Account> findAll() {
        return accountMapper.findAll();
    }
}

9.9创建controller

package com.qf.controller;

import com.qf.pojo.Account;
import com.qf.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
@RequestMapping("/account")
public class AccountController {

    @Autowired
    private AccountService accountService;

    @RequestMapping("/findAll")
    public List<Account> findAll(){

        List<Account> accounts = accountService.findAll();

        return accounts;
    }
}

9.10启动配置类,进行测试(注意:需要在启动类上添加@MapperScan扫描Mapper)

package com.qf;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.qf.mapper")
public class Springboot02Application {

    public static void main(String[] args) {
        SpringApplication.run(Springboot02Application.class, args);
    }

}

9.11 分页插件

9.11.1 导入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.13</version>
</dependency>

9.11.1 添加方法

@RequestMapping("/findByPage")
public PageInfo findByPage(@RequestParam(defaultValue = "1") Integer pageNum,
                           @RequestParam(defaultValue = "2") Integer pageSize){

    PageHelper.startPage(pageNum,pageSize);

    List<Account> accounts = accountService.findAll();

    PageInfo pageInfo = new PageInfo(accounts);

    return pageInfo;
}

启动工程,进行测试

9.12 SpringBoot工程打包部署

双击package执行打包命令,如果打包时报错,需要把pom.xml文件中的

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

改为:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>1.5.4.RELEASE</version>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.4</version>
        </plugin>
    </plugins>
</build>

然后再次打包,在对应的磁盘目录找到对应的xxx.jar文件

部署:

修改项目中的数据库相关配置,然后启动Linux上的mysql,把xxx.jar文件拷贝到Linux中的任意目录

java -jar xxx.jar Linux上启动springboot工程 java -jar -Dserver.port=1234 xxx.jar Linux上指定端口号启动springboot工程

后台启动:

使用以上命令启动成功后,输入:ctrl + z,输入:bg,输入:exit;

十、使用Druid连接池进行测试

10.1 导入依赖

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.1.10</version>
</dependency>

10.2 在application.properties文件中添加配置

#使用阿里巴巴druid数据源,默认使用自带的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

10.3 使用yml方式配置,需要创建application.yml

mybatis:
  type-aliases-package: com.qf.pojo
  mapper-locations: classpath:mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/java2001?serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

启动工程,进行测试

消息通知是现代应用中必不可少的一部分。在Spring Boot中,我们可以使用不同的技术来实现消息通知功能,如邮件通知,短信通知和推送通知等。 下面给出一些实现消息通知的技术和示例: 1. 邮件通知 Spring Boot提供了集成JavaMailSender的功能来发送邮件。您只需要在Spring Boot项目中添加相关依赖和配置即可。以下是一个简单的示例: ``` // 引入相关依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> // 邮件发送相关配置 spring.mail.host=smtp.gmail.com spring.mail.port=587 spring.mail.username=username@gmail.com spring.mail.password=password spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true // 发送邮件代码 @Autowired private JavaMailSender mailSender; public void sendMail(String to, String subject, String text) { SimpleMailMessage message = new SimpleMailMessage(); message.setTo(to); message.setSubject(subject); message.setText(text); mailSender.send(message); } ``` 2. 短信通知 您可以使用第三方短信服务提供商来发送短信通知。以下是一个简单的示例: ``` // 引入相关依赖 <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.0.3</version> </dependency> // 短信发送相关配置 spring.aliyun.sms.access-key-id=accessKeyId spring.aliyun.sms.access-key-secret=accessKeySecret spring.aliyun.sms.template-code=SMS_XXXXXXXXX spring.aliyun.sms.sign-name=XXX有限公司 // 发送短信代码 @Autowired private IAcsClient acsClient; public void sendSms(String phoneNumbers, String templateParam) { CommonRequest request = new CommonRequest(); request.setMethod(MethodType.POST); request.setDomain("dysmsapi.aliyuncs.com"); request.setVersion("2017-05-25"); request.setAction("SendSms"); request.putQueryParameter("PhoneNumbers", phoneNumbers); request.putQueryParameter("SignName", env.getProperty("spring.aliyun.sms.sign-name")); request.putQueryParameter("TemplateCode", env.getProperty("spring.aliyun.sms.template-code")); request.putQueryParameter("TemplateParam", templateParam); try { CommonResponse response = acsClient.getCommonResponse(request); System.out.println(response.getData()); } catch (ServerException e) { e.printStackTrace(); } catch (ClientException e) { e.printStackTrace(); } } ``` 3. 推送通知 推送通知可用于向应用程序的用户发送即时通知。您可以使用第三方推送服务提供商来发送推送通知,如Firebase Cloud Messaging(FCM)和苹果推送通知服务(APNS)等。以下是一个简单的示例: ``` // 引入相关依赖 <dependency> <groupId>com.google.firebase</groupId> <artifactId>firebase-admin</artifactId> <version>7.1.0</version> </dependency> // 推送通知相关配置 spring.firebase.service-account-file=serviceAccountKey.json // 发送推送通知代码 @Autowired private FirebaseMessaging firebaseMessaging; public void sendPushNotification(String token, String title, String body) throws FirebaseMessagingException { Message message = Message.builder() .setToken(token) .setNotification(Notification.builder() .setTitle(title) .setBody(body) .build()) .build(); String response = firebaseMessaging.send(message); System.out.println("Successfully sent message: " + response); } ``` 这些示例应该让您明白如何在Spring Boot中实现消息通知功能。请注意,这些示例仅仅是小型的演示示例,您需要根据您的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小yu别错过

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

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

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

打赏作者

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

抵扣说明:

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

余额充值