为了账号安全,请及时绑定邮箱和手机立即绑定

Springboot企业级开发入门教程

标签:
SpringBoot
概述

本文详细介绍了Spring Boot企业级开发的相关内容,从环境搭建到核心功能的讲解,再到数据库操作、项目部署与监控以及安全与测试,帮助开发者快速掌握Spring Boot的企业级应用开发。

Spring Boot企业级开发入门教程
Spring Boot简介与环境搭建

Spring Boot简介

Spring Boot 是由 Pivotal 团队提供的一个基于 Spring 框架的全新项目。它的主要目标是简化新 Spring 应用的初始搭建及开发过程。通过约定优于配置的方式,减少配置文件的使用,从而快速实现独立的 Spring 应用程序。Spring Boot 不需要 XML 配置,支持自动配置,简化了应用的开发流程,使开发者可以专注于业务逻辑的实现。

开发环境搭建

为了开始使用 Spring Boot,你需要一个 Java 开发环境。以下步骤将指导你如何设置开发环境:

  1. 安装 Java Development Kit (JDK)

    • 下载并安装 JDK。建议使用 JDK 8 或更高版本。
    • 设置环境变量 JAVA_HOMEPATH,确保 Java 工具能够被系统识别。例如:
      export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
      export PATH=$JAVA_HOME/bin:$PATH
  2. 安装 IntelliJ IDEA 或 Eclipse

    • 推荐使用 IntelliJ IDEA 或 Eclipse 开发 Spring Boot 应用程序。以下是使用 IntelliJ IDEA 的步骤:
      1. 下载并安装 IntelliJ IDEA。
      2. 打开 IntelliJ IDEA,创建一个新的 Maven 项目。
      3. 在 Maven 项目配置中,确保添加了 Spring Boot 的依赖。编辑 pom.xml 文件,加入 Spring Boot 的依赖配置:
        <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>
        </dependencies>
        <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.4.RELEASE</version>
            </plugin>
        </plugins>
        </build>
    • 如果使用 Eclipse,步骤类似。安装 Eclipse 后,创建一个 Maven 项目,同样需要在 pom.xml 文件中添加 Spring Boot 的依赖。
  3. 安装 Maven

    • Maven 是一个强大的项目管理和构建工具。你可以从 Maven 官方网站下载并按照说明进行安装。
    • 设置环境变量 MAVEN_HOMEPATH,确保 Maven 工具能够被系统识别。例如:
      export MAVEN_HOME=/path/to/maven
      export PATH=$MAVEN_HOME/bin:$PATH
  4. 安装 Spring Boot CLI(可选)
    • Spring Boot CLI 是一个命令行工具,可以帮助你快速创建和运行 Spring Boot 应用程序。
    • 使用命令行安装:mvn install -DskipTests -Dmaven.test.skip=true

快速搭建第一个 Spring Boot 项目

这里我们使用 IntelliJ IDEA 来创建第一个 Spring Boot 项目。

  1. 打开 IntelliJ IDEA,选择 File -> New -> Project
  2. 在弹出的窗口中,选择 Maven,勾选 Create from archetype,并选择 maven-archetype-quickstart
  3. 填写 Group ID 和 Artifact ID。例如:
    • Group Id: com.example
    • Artifact Id: my-spring-boot-app
  4. 点击 Next,然后点击 Finish

在创建的项目中,会有一个 pom.xml 文件,你需要在该文件中添加 Spring Boot 的依赖。编辑 pom.xml 文件,加入 Spring Boot 的依赖配置:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
       .
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.3.4.RELEASE</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.3.4.RELEASE</version>
        </plugin>
    </plugins>
</build>

完成配置后,你可以在 src/main/java 目录下创建一个新的 Java 类,比如 Application.java,并编写启动类:

package com.example.mySpringBootApp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

src/main/resources 目录下创建 application.properties,你可以在这里配置应用程序的属性:

server.port=8080

最后,运行应用程序。在 IntelliJ IDEA 中,右键点击 Application 类并选择 Run 'Application.main()',程序将启动,并监听 8080 端口。

Spring Boot 核心功能介绍

自动配置

Spring Boot 提供了许多自动配置功能,使开发者无需手动配置大量的 Bean。例如,当你在 pom.xml 中添加 spring-boot-starter-web 依赖时,Spring Boot 会自动配置一个 Tomcat 服务器、Spring MVCDispatcherServlet

依赖管理

依赖管理是 Maven 和 Gradle 的一个高级功能。Spring Boot 提供了一个 spring-boot-starter-parent,它定义了所有依赖的默认版本。开发者只需要在 pom.xml 文件中指定依赖的 groupIdartifactId,Spring Boot 会自动设置版本号。

例如,以下代码展示了如何添加 spring-boot-starter-jdbc 依赖:

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

启动器

启动器是 Spring Boot 项目的核心组件。每个启动器都提供了很多依赖,帮助开发者更快地开发应用程序。例如,spring-boot-starter-web 包含了开发 Web 应用程序所需的依赖,spring-boot-starter-data-jpa 则包含了开发 JPA 应用程序所需的依赖。

Spring Boot 常用注解详解

@SpringBootApplication

@SpringBootApplication 是一个复合注解,整合了多个注解的功能。它包含以下注解:

  • @Configuration:标记一个类作为配置类。
  • @EnableAutoConfiguration:启用 Spring Boot 的自动配置功能。
  • @ComponentScan:扫描并注册标注了 @Component@Service@Repository@Controller 的类。

例如:

package com.example.mySpringBootApp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

@Controller / @Service / @Repository / @Component

  • @Controller:用于处理 HTTP 请求的类。通常与 @RequestMapping 注解一起使用,指定处理 URL 请求的方法。
  • @Service:标记一个类为服务层组件。例如,一个实现了业务逻辑的类。
  • @Repository:标记一个类为数据访问层组件。例如,一个实现了数据访问的类。
  • @Component:通用的组件注解。可以用于任何需要标注为 Spring 组件的类。

例如:

package com.example.mySpringBootApp;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {
    // 数据访问逻辑
}

@RestController

@RestController@Controller@ResponseBody 的组合。标记一个 RESTful 风格的控制器。

例如:

package com.example.mySpringBootApp;

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

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String sayHello() {
        return "Hello, World!";
    }
}
Spring Boot 数据库操作

JPA 与 MyBatis

Spring Boot 支持多种持久化框架,包括 JPA 和 MyBatis。JPA 是 Java Persistence API 的缩写,它提供了对数据库的高级抽象,而 MyBatis 是一个持久层框架,提供了一种更加灵活的 SQL 映射方式。

数据库配置与连接

为了配置数据库连接,你需要在 application.properties 文件中添加数据库连接的配置信息。例如,以下是连接到 MySQL 数据库的配置:

spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update

实战:CRUD 操作

以下是一些 CRUD 操作的示例代码:

  1. 创建实体类
    
    package com.example.mySpringBootApp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;

// Getters and Setters

}


2. **创建 Repository 接口**
```java
package com.example.mySpringBootApp;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}
  1. 创建 Service 类
    
    package com.example.mySpringBootApp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
@Autowired
private UserRepository userRepository;

public List<User> getAllUsers() {
    return userRepository.findAll();
}

public User getUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}

public User createUser(User user) {
    return userRepository.save(user);
}

public User updateUser(Long id, User user) {
    User existingUser = userRepository.findById(id).orElse(null);
    if (existingUser != null) {
        existingUser.setName(user.getName());
        existingUser.setEmail(user.getEmail());
        return userRepository.save(existingUser);
    }
    return null;
}

public void deleteUser(Long id) {
    userRepository.deleteById(id);
}

}


4. **创建 Controller 类**
```java
package com.example.mySpringBootApp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUser(id, user);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

数据库配置与连接(MyBatis)

同样地,你也可以使用 MyBatis 进行数据库操作。以下是 MyBatis 的配置和使用示例:

  1. 添加 MyBatis 依赖

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mybatis</artifactId>
    </dependency>
  2. 创建 MyBatis 配置文件
    resources 目录下创建 mybatis-config.xml 文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <typeAliases>
        <typeAlias type="com.example.mySpringBootApp.User" alias="User"/>
    </typeAliases>
    </configuration>
  3. 配置 application.properties

    spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
    spring.datasource.username=root
    spring.datasource.password=root
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    mybatis.mapper-locations=classpath:mapper/*.xml
  4. 创建 Mapper 接口
    
    package com.example.mySpringBootApp;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
User getUserById(Long id);
List<User> getAllUsers();
User createUser(User user);
User updateUser(Long id, User user);
void deleteUser(Long id);
}


5. **创建 Mapper XML 文件**
在 `resources/mapper` 目录下创建 `UserMapper.xml` 文件:
```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.example.mySpringBootApp.UserMapper">
    <select id="getUserById" resultType="User">
        SELECT * FROM users WHERE id = #{id}
    </select>
    <select id="getAllUsers" resultType="User">
        SELECT * FROM users
    </select>
    <insert id="createUser" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users (name, email) VALUES (#{name}, #{email})
    </insert>
    <update id="updateUser">
        UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
    </update>
    <delete id="deleteUser">
        DELETE FROM users WHERE id = #{id}
    </delete>
</mapper>
  1. 创建 Service 类
    
    package com.example.mySpringBootApp;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
@Autowired
private SqlSession sqlSession;

public List<User> getAllUsers() {
    return sqlSession.selectList("com.example.mySpringBootApp.UserMapper.getAllUsers");
}

public User getUserById(Long id) {
    return sqlSession.selectOne("com.example.mySpringBootApp.UserMapper.getUserById", id);
}

public User createUser(User user) {
    sqlSession.insert("com.example.mySpringBootApp.UserMapper.createUser", user);
    return user;
}

public User updateUser(Long id, User user) {
    sqlSession.update("com.example.mySpringBootApp.UserMapper.updateUser", user);
    return user;
}

public void deleteUser(Long id) {
    sqlSession.delete("com.example.mySpringBootApp.UserMapper.deleteUser", id);
}

}


7. **创建 Controller 类**
```java
package com.example.mySpringBootApp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUser(id, user);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}
Spring Boot 项目部署与监控

部署到 Tomcat

要将 Spring Boot 应用程序部署到 Tomcat,你需要创建一个 war 文件。以下是步骤:

  1. 修改 pom.xml

    <packaging>war</packaging>
  2. 修改启动类
    
    package com.example.mySpringBootApp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}


3. **打包应用程序**
使用 Maven 打包:
```bash
mvn clean package

这将生成一个 war 文件,位于 target 目录下。

  1. 部署到 Tomcat
    将生成的 war 文件部署到 Tomcat 服务器。将 war 文件复制到 Tomcat 的 webapps 目录下,Tomcat 将自动解压并启动应用。

使用 Actuator 监控应用

Spring Boot Actuator 提供了一系列的生产就绪功能,如监控、健康检查、配置值、环境信息、HTTP 跟踪等。

激活 Actuator

pom.xml 文件中添加 spring-boot-starter-actuator 依赖:

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

配置 Actuator 端点

application.properties 文件中启用某些端点:

management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always

使用 Actuator 端点

启动应用程序后,你可以通过访问以下 URL 来使用 Actuator 端点:

  • http://localhost:8080/actuator:列出所有可用的端点。
  • http://localhost:8080/actuator/health:获取应用的健康状态。
  • http://localhost:8080/actuator/info:获取应用的配置信息。

例如,访问 http://localhost:8080/actuator/health 可以看到如下输出:

{
  "status": "UP",
  "components": {
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 380835217408,
        "free": 269892854784,
        "threshold": 10485760,
        "exists": true
      }
    }
  }
}
Spring Boot 安全与测试

Spring Security 基础

Spring Security 是一个强大的安全框架,用于保护 Web 应用程序。它提供了多种安全功能,包括认证、授权以及 CSRF 防护等。

配置 Spring Security

pom.xml 文件中添加 spring-boot-starter-security 依赖:

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

在应用程序主类中添加 @EnableWebSecurity 注解,并创建一个安全配置类:

package com.example.mySpringBootApp;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

创建登录页面

src/main/resources/templates 目录下创建一个 login.html 文件:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login Page</title>
</head>
<body>
    <h1>Login</h1>
    <form method="post" th:action="@{/login}">
        <p>
            <label for="username">Username:</label>
            <input type="text" id="username" name="username"/>
        </p>
        <p>
            <label for="password">Password:</label>
            <input type="password" id="password" name="password"/>
        </p>
        <button type="submit">Login</button>
    </form>
    <p th:if="${param.error}">Invalid username or password.</p>
    <p th:if="${param.logout}">You have been logged out.</p>
</body>
</html>

单元测试与集成测试

Spring Boot 支持多种测试工具,包括 JUnit、Spring Test、Mockito 等。以下是编写单元测试和集成测试的示例:

单元测试示例

单元测试主要关注代码的单元级功能,例如服务层和数据访问层的测试。

package com.example.mySpringBootApp;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest
public class UserServiceTests {
    @Autowired
    private UserService userService;

    @Test
    public void testGetAllUsers() {
        List<User> users = userService.getAllUsers();
        assertEquals(0, users.size());
    }

    @Test
    public void testCreateUser() {
        User user = new User();
        user.setName("Alice");
        user.setEmail("alice@example.com");
        User createdUser = userService.createUser(user);
        assertEquals("Alice", createdUser.getName());
    }

    @Test
    public void testGetUserById() {
        User user = new User();
        user.setName("Bob");
        user.setEmail("bob@example.com");
        User createdUser = userService.createUser(user);

        User retrievedUser = userService.getUserById(createdUser.getId());
        assertEquals("Bob", retrievedUser.getName());
    }

    @Test
    public void testUpdateUser() {
        User user = new User();
        user.setName("Charlie");
        user.setEmail("charlie@example.com");
        User createdUser = userService.createUser(user);

        createdUser.setName("Charlie Updated");
        User updatedUser = userService.updateUser(createdUser.getId(), createdUser);
        assertEquals("Charlie Updated", updatedUser.getName());
    }

    @Test
    public void testDeleteUser() {
        User user = new User();
        user.setName("David");
        user.setEmail("david@example.com");
        User createdUser = userService.createUser(user);

        userService.deleteUser(createdUser.getId());
        User retrievedUser = userService.getUserById(createdUser.getId());
        assertEquals(null, retrievedUser);
    }
}

集成测试示例

集成测试主要测试应用程序的不同组件之间的交互,例如控制器、服务和数据访问层。


package com.example.mySpringBootApp;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

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

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest
public class UserControllerTests {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    public void testGetAllUsers() throws Exception {
        List<User> users = Arrays.asList(new User(1L, "Alice", "alice@example.com"), new User(2L, "Bob", "bob@example.com"));
        when(userService.getAllUsers()).thenReturn(users);

        mockMvc.perform(MockMvcRequestBuilders.get("/users"))
            .andExpect(status().isOk())
            .andExpect(MockMvcResultMatchers.jsonPath("$.[0].name").value("Alice"))
            .andExpect(MockMvcResultMatchers.jsonPath("$.[1].name").value("Bob"));
    }

    @Test
    public void testGetUserById() throws Exception {
        User user = new User(1L, "Charlie", "charlie@example.com");
        when(userService.getUserById(1L)).thenReturn(user);

        mockMvc.perform(MockMvcRequestBuilders.get("/users/1"))
            .andExpect(status().isOk())
            .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Charlie"));
    }

    @Test
    public void testCreateUser() throws Exception {
        User user = new User();
        user.setName("David");
        user.setEmail("david@example.com");

        User createdUser = new User(1L, "David", "david@example.com");
        when(userService.createUser(user)).thenReturn(createdUser);

        mockMvc.perform(MockMvcRequestBuilders.post("/users")
                .contentType("application/json")
                .content("{ \"name\": \"David\", \"email\": \"david@example.com\" }"))
            .andExpect(status().isOk())
            .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("David"));
    }

    @Test
    public void testUpdateUser() throws Exception {
        User user = new User();
        user.setName("Eve");
        user.setEmail("eve@example.com");

        User updatedUser = new User(1L, "Eve Updated", "eve@example.com");
        when(userService.updateUser(1L, user)).thenReturn(updatedUser);

        mockMvc.perform(MockMvcRequestBuilders.put("/users/1")
                .contentType("application/json")
                .content("{ \"name\": \"Eve Updated\", \"email\": \"eve@example.com\" }"))
            .andExpect(status().isOk())
            .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Eve Updated"));
    }

    @Test
    public void testDeleteUser() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.delete("/users/1"))
            .andExpect(status().isOk());
    }
}
``

以上是 Spring Boot 企业级开发入门教程的完整内容。希望这些内容能够帮助你快速上手 Spring Boot,并开发出高质量的 Java 应用程序。
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消