继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java全栈入门:从零开始的全栈开发教程

Qyouu
关注TA
已关注
手记 444
粉丝 87
获赞 413
概述

Java全栈入门涵盖了从基础环境搭建到前后端开发的全面内容,帮助初学者系统地掌握Java全栈开发技能。本文详细介绍了Java基础语法、面向对象编程、常用类库以及后端开发和前端开发的相关知识。此外,还包括了项目实战、开发工具与调试技巧,以及进阶与拓展内容如微服务架构和Docker容器化部署。

Java基础入门

Java环境搭建

为了开始学习Java编程,你需要首先搭建开发环境。以下是搭建步骤:

  1. 下载JDK:首先从Oracle官网或其他可靠源下载Java开发工具包(JDK)。请确保选择适合你的操作系统版本。
  2. 安装JDK:安装JDK时,注意记录JDK的安装路径。
  3. 配置环境变量
    • Windows系统:在系统环境变量中添加JAVA_HOME指向JDK安装目录,并将%JAVA_HOME%\bin添加到Path变量中。
    • Mac/Linux:在终端中编辑~/.bashrc~/.zshrc文件,添加以下行:
      export JAVA_HOME=/path/to/java
      export PATH=$JAVA_HOME/bin:$PATH
  4. 验证安装:打开命令行工具,输入java -version,如果显示版本信息则表示安装成功。

Java基本语法

变量与类型

在Java中,变量是用于存储数据值的标识符。变量有类型,类型决定了变量可以存储的数据类型。以下是Java中常见的基本数据类型:

  • int:32位整数
  • long:64位整数
  • float:32位单精度浮点数
  • double:64位双精度浮点数
  • boolean:真或假
  • char:单个字符
  • String:字符串

示例代码:

public class VariableDemo {
    public static void main(String[] args) {
        int integer = 10;
        long bigInteger = 1234567890123456789L;
        float decimal = 3.14f;
        double precision = 3.1415926;
        boolean flag = true;
        char singleChar = 'a';
        String text = "Hello, World!";

        System.out.println("Integer: " + integer);
        System.out.println("Long: " + bigInteger);
        System.out.println("Float: " + decimal);
        System.out.println("Double: " + precision);
        System.out.println("Boolean: " + flag);
        System.out.println("Char: " + singleChar);
        System.out.println("String: " + text);
    }
}
``

#### 基础语法结构

Java程序由若干个类组成,每个类可以包含方法(函数)和成员变量。Java程序的主入口是`public static void main(String[] args)`方法。

示例代码:

```java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Java面向对象编程

面向对象编程(OOP)是Java的核心特性之一。Java中的类(Class)是面向对象编程的基石。类定义了对象的属性和行为,而对象则是类的实例。

类与对象

类定义了对象的属性和行为,对象则是这些定义的实际实例。在定义一个类时,通常包含成员变量和方法。

示例代码:

public class Person {
    // 成员变量
    public String name;
    public int age;

    // 构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 方法
    public void introduce() {
        System.out.println("My name is " + name + " and I am " + age + " years old.");
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person("Alice", 25);
        person.introduce();
    }
}

继承与多态

继承允许你创建一个新类,继承另一个类的属性和方法。多态则允许你使用一个父类型引用指向一个子类型对象。

示例代码:

public class Animal {
    public void sound() {
        System.out.println("Animal sound");
    }
}

public class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("Woof!");
    }
}

public class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("Meow!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.sound();

        Animal dog = new Dog();
        dog.sound();

        Animal cat = new Cat();
        cat.sound();
    }
}

Java常用类库介绍

Java提供了丰富的类库,如java.utiljava.lang。这些库提供了许多常用的功能,如集合框架、日期操作、字符串处理等。

集合框架

集合框架用于存储和操作一组对象。Java提供了多种集合接口和实现类,如List, Set, 和Map

示例代码:

import java.util.ArrayList;
import java.util.List;

public class CollectionDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Banana");
        list.add("Cherry");

        for (String fruit : list) {
            System.out.println(fruit);
        }
    }
}

日期操作

Java 8 引入了新的日期和时间API。这些API在java.time包中,提供了更强大的日期和时间操作功能。

示例代码:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateDemo {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        System.out.println("Today's date: " + today);

        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedDateTime = now.format(formatter);
        System.out.println("Formatted date and time: " + formattedDateTime);
    }
}
后端开发入门

Java Web开发基础

Java Web开发是基于Servlet API和JSP技术构建的。Servlet是Java编写的服务器端程序,用于处理HTTP请求和响应。

Servlet与JSP

Servlet是Java Web应用的核心。JSP (JavaServer Pages)是一种用来呈现动态内容的HTML格式。

示例代码:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>Hello Servlet</title></head>");
        out.println("<body>");
        out.println("<h1>Hello, World!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

Spring Boot快速入门

Spring Boot提供了一种快速开发Spring应用的方式,它自动配置了大量常用的功能。

创建Spring Boot应用

创建一个简单的Spring Boot应用,包含控制器和简单的REST接口。

示例代码:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
class GreetingController {
    @GetMapping("/greeting")
    public String greeting() {
        return "Hello, Spring Boot!";
    }
}

数据库基础与MySQL操作

连接MySQL数据库

使用JDBC连接MySQL数据库。首先,需要添加数据库驱动依赖,然后建立数据库连接。

示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class DatabaseDemo {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "password";

        try {
            Connection connection = DriverManager.getConnection(url, user, password);
            Statement statement = connection.createStatement();
            String sql = "CREATE TABLE Users (id INT, name VARCHAR(255))";
            statement.executeUpdate(sql);
            System.out.println("Table created successfully!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

CRUD操作

创建、读取、更新和删除数据库中的数据。

示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class CRUDDemo {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "password";

        try (Connection connection = DriverManager.getConnection(url, user, password)) {
            String sql = "INSERT INTO Users (id, name) VALUES (?, ?)";
            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setInt(1, 1);
            statement.setString(2, "Alice");
            statement.executeUpdate();

            sql = "SELECT * FROM Users WHERE id = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, 1);
            ResultSet resultSet = statement.executeQuery();
            while (resultSet.next()) {
                System.out.println("ID: " + resultSet.getInt("id") + ", Name: " + resultSet.getString("name"));
            }

            sql = "UPDATE Users SET name = ? WHERE id = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1, "Bob");
            statement.setInt(2, 1);
            statement.executeUpdate();

            sql = "DELETE FROM Users WHERE id = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, 1);
            statement.executeUpdate();

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

RESTful API设计与实现

RESTful API是一种设计网络应用的架构风格,它使用HTTP协议进行数据传输,并通过资源的URL来标识。

示例代码:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
class UserController {
    @GetMapping("/users")
    public String getUsers() {
        return "[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]";
    }
}
前端开发入门

HTML与CSS基础

HTML(HyperText Markup Language)是用于构建网页的标记语言,而CSS(Cascading Style Sheets)用于定义网页的样式。

基础标签

HTML基本结构:

<!DOCTYPE html>
<html>
<head>
    <title>Page Title</title>
</head>
<body>
    <h1>My First Heading</h1>
    <p>My first paragraph.</p>
</body>
</html>

基础样式

使用CSS定义样式:

<!DOCTYPE html>
<html>
<head>
    <style>
        body {
            background-color: powderblue;
        }
        h1 {
            color: blue;
            text-align: center;
        }
        p {
            font-family: verdana;
            font-size: 20px;
        }
    </style>
</head>
<body>
    <h1>My First Heading</h1>
    <p>My first paragraph.</p>
</body>
</html>

JavaScript基础

JavaScript是一种用于添加交互性的编程语言。它可以操作DOM(文档对象模型)来动态修改网页。

基础语法

<!DOCTYPE html>
<html>
<head>
    <title>JavaScript Example</title>
</head>
<body>
    <button onclick="changeText()">Change Text</button>

    <script>
        function changeText() {
            document.getElementById("demo").innerHTML = "Hello, JavaScript!";
        }
    </script>
</body>
</html>

DOM操作

使用JavaScript操作DOM:

<!DOCTYPE html>
<html>
<head>
    <title>DOM Example</title>
</head>
<body>
    <h1 id="demo">Initial Text</h1>

    <script>
        document.getElementById("demo").innerHTML = "Modified Text";
    </script>
</body>
</html>

前端框架Vue.js简介

Vue.js是一个渐进式前端框架,它允许你逐步将Vue引入现有项目或从零开始构建单页面应用。

安装Vue.js

<!DOCTYPE html>
<html>
<head>
    <title>Vue.js Example</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        {{ message }}
    </div>

    <script>
        var app = new Vue({
            el: '#app',
            data: {
                message: 'Hello Vue!'
            }
        });
    </script>
</body>
</html>

与后端交互

使用Vue.js与后端交互:

<!DOCTYPE html>
<html>
<head>
    <title>Vue.js with Backend</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        {{ greeting }}
    </div>

    <script>
        var app = new Vue({
            el: '#app',
            data: {
                greeting: ''
            },
            mounted() {
                axios.get('/api/greeting')
                    .then(response => {
                        this.greeting = response.data;
                    })
                    .catch(error => {
                        console.error(error);
                    });
            }
        });
    </script>
</body>
</html>

前后端数据交互

前后端数据交互通常通过HTTP请求实现。前端使用JavaScript库如Axios发起请求,后端使用RESTful API响应。

示例代码

前端:

<!DOCTYPE html>
<html>
<head>
    <title>Frontend Backend Interaction</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        {{ greeting }}
    </div>

    <script>
        var app = new Vue({
            el: '#app',
            data: {
                greeting: ''
            },
            mounted() {
                axios.get('/api/greeting')
                    .then(response => {
                        this.greeting = response.data;
                    })
                    .catch(error => {
                        console.error(error);
                    });
            }
        });
    </script>
</body>
</html>

后端(Spring Boot):

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
class GreetingController {
    @GetMapping("/api/greeting")
    public String greeting() {
        return "Hello, Frontend!";
    }
}
项目实战

项目规划与需求分析

项目规划包括确定项目的目标、范围、时间表和技术栈。需求分析则是明确项目的需求,包括功能需求和非功能需求。

示例项目

项目目标:构建一个简单的图书管理系统。
功能需求:用户可以浏览、添加、编辑和删除图书。
非功能需求:系统需要保证数据的安全性和完整性。

示例代码:

前端(Vue.js):

<!DOCTYPE html>
<html>
<head>
    <title>图书管理系统</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        <h1>图书列表</h1>
        <ul>
            <li v-for="book in books" :key="book.id">
                {{ book.title }} - {{ book.author }}
            </li>
        </ul>
        <button @click="addBook()">添加图书</button>
    </div>

    <script>
        var app = new Vue({
            el: '#app',
            data: {
                books: []
            },
            methods: {
                getBooks() {
                    axios.get('/api/books')
                        .then(response => {
                            this.books = response.data;
                        })
                        .catch(error => {
                            console.error(error);
                        });
                },
                addBook() {
                    // 添加图书逻辑
                }
            },
            mounted() {
                this.getBooks();
            }
        });
    </script>
</body>
</html>

后端(Spring Boot):

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

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

@RestController
class BookController {
    private List<Book> books = new ArrayList<>();

    @GetMapping("/api/books")
    public List<Book> getBooks() {
        return books;
    }

    @PostMapping("/api/books")
    public void addBook(@RequestBody Book book) {
        books.add(book);
    }
}

class Book {
    private int id;
    private String title;
    private String author;

    // Getters and Setters
}

构建全栈项目框架

全栈项目通常包含前端、后端和数据库三个部分。前端负责用户交互,后端负责处理业务逻辑和数据交互,数据库则负责存储数据。

构建步骤

  1. 前端:使用Vue.js构建前端界面。
  2. 后端:使用Spring Boot构建后端服务。
  3. 数据库:使用MySQL存储图书信息。

示例代码

详见“项目规划与需求分析”部分。

前后端联调与测试

前后端联调是指前端和后端互相配合,确保前后端对接的接口可以正确通信。测试则是确保每个接口和功能按预期运行。

示例测试

前端测试(使用Jest):

import { shallowMount } from '@vue/test-utils';
import App from './App.vue';

describe('App.vue', () => {
    it('renders correct text', () => {
        const wrapper = shallowMount(App);
        expect(wrapper.text()).toMatch('图书列表');
    });
});

后端测试(使用JUnit):

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.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(BookController.class)
public class BookControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        mockMvc.perform(get("/api/books"))
            .andExpect(status().isOk())
            .andExpect(content().string("[]"));
    }
}

项目部署与上线

项目部署是指将开发好的应用部署到生产环境。上线则是确保应用在生产环境稳定运行。

部署步骤

  1. 打包应用:将前端和后端分别打包为可执行文件。
  2. 配置服务器:准备服务器环境,包括操作系统、Java环境、数据库等。
  3. 上传应用:将打包好的应用上传到服务器。
  4. 启动应用:启动应用并进行测试。

示例代码:

# 打包前端应用
npm run build

# 打包后端应用
mvn clean package

# 配置服务器
# 服务器环境配置示例

# 上传应用
scp target/myapp.jar user@server:/path/to/deploy

# 启动应用
java -jar /path/to/deploy/myapp.jar
开发工具与调试

常用IDE介绍与配置

IDE(集成开发环境)是开发者的得力助手。常用的Java IDE包括Eclipse、IntelliJ IDEA和NetBeans。这里详细介绍Eclipse和IntelliJ IDEA配置。

Eclipse

  1. 下载并安装Eclipse:从Eclipse官网下载并安装Eclipse。
  2. 安装Java插件:通过Eclipse Marketplace安装Java开发插件。
  3. 配置项目:创建新项目并配置Java环境。

IntelliJ IDEA

  1. 下载并安装IntelliJ IDEA:从官网下载并安装IntelliJ IDEA。
  2. 安装Java插件:启动IntelliJ IDEA后,安装Java相关插件。
  3. 配置项目:创建新项目并配置Java环境。

示例配置

Eclipse:

  1. 打开Eclipse,选择File -> New -> Java Project
  2. 输入项目名称,点击Finish
  3. 右键项目,选择Build Path -> Configure Build Path,添加JDK路径。

IntelliJ IDEA:

  1. 打开IntelliJ IDEA,选择File -> New Project
  2. 选择Java,点击Next
  3. 输入项目名称,点击Finish
  4. 右键项目,选择Open Module Settings,添加JDK路径。

版本控制与Git使用

使用版本控制可以跟踪代码的变更历史,方便多人协作。Git是最常用的版本控制系统。

基本操作

  1. 初始化仓库:使用git init命令初始化仓库。
  2. 提交代码:使用git commit命令提交代码。
  3. 推送代码:使用git push命令将本地仓库的代码推送到远程仓库。

示例代码

# 初始化仓库
git init

# 添加文件到仓库
git add .

# 提交代码
git commit -m "Initial commit"

# 推送代码到远程仓库
git remote add origin https://github.com/username/repository.git
git push -u origin master

调试技巧与常见错误处理

调试是发现并修复程序错误的过程。有效的调试技巧可以提高开发效率。

调试技巧

  1. 设置断点:在代码关键位置设置断点,程序运行到断点时会暂停。
  2. 查看变量值:检查变量值以确定程序状态。
  3. 单步执行:逐行执行代码以查看每一步的执行情况。

常见错误处理

  1. NullPointerException:尝试访问空对象的成员。
  2. ClassCastException:尝试将一个对象转换为它不是的类型。
  3. ArrayIndexOutOfBoundsException:访问数组无效索引。

示例代码

public class DebugExample {
    public static void main(String[] args) {
        String[] names = {"Alice", "Bob", "Charlie"};
        for (int i = 0; i <= names.length; i++) {
            System.out.println(names[i]);
        }
    }
}
进阶与拓展

设计模式初步

设计模式是解决特定问题的模板。常见的设计模式包括单例模式、工厂模式和观察者模式。

单例模式

单例模式确保一个类只有一个实例,并提供一个全局访问点。

示例代码:

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

工厂模式

工厂模式用于创建对象,将对象的创建与使用分离,提高代码的灵活性。

示例代码:

interface Shape {
    void draw();
}

class Circle implements Shape {
    public void draw() {
        System.out.println("Drawing a Circle");
    }
}

class Square implements Shape {
    public void draw() {
        System.out.println("Drawing a Square");
    }
}

class ShapeFactory {
    public Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}

public class FactoryExample {
    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();
        Shape circle = shapeFactory.getShape("CIRCLE");
        circle.draw();

        Shape square = shapeFactory.getShape("SQUARE");
        square.draw();
    }
}

微服务架构简介

微服务架构将一个大型应用拆分为多个小型服务,每个服务独立部署、扩展和维护。

微服务优势

  1. 独立性:每个服务可以独立开发、测试和部署。
  2. 灵活性:可以使用不同的技术栈构建服务。
  3. 可扩展性:可以按需扩展服务,提高系统性能。

示例架构

假设有一个电商应用,可以拆分为订单服务、支付服务、库存服务等多个微服务。

示例代码:

@RestController
class OrderController {
    @PostMapping("/orders")
    public void createOrder(@RequestBody Order order) {
        // 创建订单逻辑
    }
}

class Order {
    // 订单相关属性
}

Docker与容器化部署

Docker是一种容器化技术,可以将应用及其依赖打包在一个容器中,便于部署和迁移。

基本操作

  1. 创建Dockerfile:定义应用的构建和运行环境。
  2. 构建镜像:使用docker build命令构建镜像。
  3. 运行容器:使用docker run命令运行容器。

示例代码

Dockerfile:

FROM openjdk:8-jdk-alpine
COPY target/myapp.jar myapp.jar
ENTRYPOINT ["java", "-jar", "myapp.jar"]

构建并运行:

# 构建镜像
docker build -t myapp .

# 运行容器
docker run -p 8080:8080 myapp

持续集成与持续部署

持续集成和持续部署(CI/CD)是现代软件开发的重要实践。CI确保代码质量,CD确保快速部署。

CI/CD流程

  1. 代码提交:开发人员提交代码到版本控制系统。
  2. 构建与测试:自动构建并运行测试。
  3. 部署:将通过测试的代码部署到生产环境。

示例工具

  • Jenkins:CI/CD工具,用于自动化构建、测试和部署。
  • GitLab CI/CD:集成在GitLab中的CI/CD工具。

示例配置

Jenkins Pipeline:

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
        stage('Deploy') {
            steps {
                sh 'mvn deploy'
            }
        }
    }
}

GitLab CI/CD:

stages:
  - build
  - test
  - deploy

build:
  stage: build
script:
  - mvn clean package

test:
  stage: test
  script:
  - mvn test

deploy:
  stage: deploy
  script:
  - mvn deploy
  only:
  - master
打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP