简单了解thymeleaf

最近花了点时间了解了一下Thymeleaf的简单应用,毕竟现在jsp大势已去,spring官方也在推Thymeleaf,简单的掌握一点知识,以后会用到。而且现在公司项目中已经涉及到了,虽然比较简单。

这个文章大概参考了两篇文章,一篇是:入门例子;另一篇是:表单提交

主要涉及到的标签只有,th:text——取值,th:object——取对象,th:if——条件判断,th:each——循环取值,th:action——form表单中的地址,th:method——form表单中的方法。

而这些标签中又涉及到几种取值表达式:${}这种一般在取后台数据的时候用到;*{}这种一般在后台取到对象后,单独取对象中的每个属性值时用到;#{}这种一般就是取静态默认属性值,如在application文件中定义好的属性值。

我们在接下来的内容中一一介绍。这里是该项目的Github地址

pom.xml文件中涉及的依赖如下:

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

默认的Thymeleaf模板页面应该放在resources/templates目录中。我们在该目录下新建一个index.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>index</title>
</head>
<body>
<h1>Thymeleaf Page: Welcome to Spring Boot World!</h1>
<h1>File Location: resources/templates/index.html</h1>
</body>
</html>

然后我们新建一个HomeController,并新建一个方法来访问该文件。

这里写图片描述

启动服务,看能否访问该页面。

这里写图片描述

接着我们通过设置thymeleaf前缀来修改文件的访问路径。

我们新建一个index.html文件,在webapp/WEB-INF/page目录下。

这里写图片描述

文件内容做了点小改动:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>index</title>
</head>
<body>
<h1>Thymeleaf Page: Welcome to Spring Boot World!</h1>
<h1>File Location: webapp/WEB-INF/page/index.html</h1>
</body>
</html>

然后在application文件中增加配置:

spring.thymeleaf.prefix=/WEB-INF/page/

再次访问,看看我们访问的是哪个路径下的文件:

这里写图片描述

可以看到访问路径发生了改变。

静态值的获取

我们在application文件中增加几个静态值:

student.id=1
student.name=Agumon
student.gender=male
student.age=3
student.telephone=8848

接着我们新建一个页面来获取这些静态值:

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title>显示学生信息</title>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<h2>#{}直接可以取对应的属性值(如在application.properties中定义的相关属性)</h2>
编号:<span th:text="#{student.id}">2</span><br>
姓名:<span th:text="#{student.name}">mnnnb</span><br>
性别:<span th:text="#{student.gender}"></span><br>
年龄:<span th:text="#{student.age}">20</span><br>
电话:<span th:text="#{student.telephone}">777777777</span><br>

</html>

同时在HomeController中新增一个方法来访问这个页面:

这里写图片描述

然后我们先访问一下静态页面的展示,用如下方式:

这里写图片描述

然后可以看到页面内容

这里写图片描述

之后我们用接口访问,看一下页面是否发生了变化:

这里写图片描述

好像有点问题,我们需要做些配置:

package com.yubotao.thymeleaf.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 10:27 2018/9/7
 * @Modified By:
 */
@Configuration
public class I18NConfig {
    @Bean
    public ResourceBundleMessageSource messageSource(){
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setFallbackToSystemLocale(false);
        // 设置消息源,表明消息源是以application打头属性文件
        messageSource.setBasename("application");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(2);
        return messageSource;
    }
}

再访问,我们可以看到能够正确显示了

这里写图片描述

可以看到取值发生变化,我们通过#{}取到了application文件中定义的静态属性值。

然后我们在resources中在创建一个application_zh_CN.properties,因为我们之前的配置类是国际化配置,所以可以根据系统语言环境取对应的版本属性文件。

#由于系统语言环境原因,该文件会先于application.properties读取

student.id=1
student.name=郭文玲
student.gender=女
student.age=18
student.telephone=15890904568

访问看结果

这里写图片描述

接着我们来看看如何获取后台传过来的动态数据。

从后台接口获取数据

创建用户实体类User

package com.yubotao.thymeleaf.model;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.util.Date;

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 10:39 2018/9/7
 * @Modified By:
 */
@ToString
public class User {
    @Getter
    @Setter
    private int id;
    @Getter
    @Setter
    private String username;
    @Getter
    @Setter
    private String password;
    @Getter
    @Setter
    private String telephone;
    @Getter
    @Setter
    private Date registerTime;
    @Getter
    @Setter
    private int popedom;  // 权限 0-管理员 1-普通用户
}

这里伪造查询方法UserService

package com.yubotao.thymeleaf.service;

import com.yubotao.thymeleaf.model.User;
import org.springframework.stereotype.Service;

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

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 10:43 2018/9/7
 * @Modified By:
 */
@Service
public class UserService {

    public User findOneUser() {
        User user = new User();
        user.setId(1);
        user.setUsername("李文强");
        user.setPassword("12345");
        user.setTelephone("15890904567");
        user.setRegisterTime(new Date());
        user.setPopedom(0);
        return user;
    }

    public List<User> findAllUsers() {
        List<User> users = new ArrayList<User>();

        User user = new User();
        user.setId(1);
        user.setUsername("李文强");
        user.setPassword("12345");
        user.setTelephone("15890904567");
        user.setRegisterTime(new Date());
        user.setPopedom(0);
        users.add(user);

        user = new User();
        user.setId(2);
        user.setUsername("张海洋");
        user.setPassword("11111");
        user.setTelephone("13990904567");
        user.setRegisterTime(new Date());
        user.setPopedom(1);
        users.add(user);

        user = new User();
        user.setId(3);
        user.setUsername("吴文燕");
        user.setPassword("22222");
        user.setTelephone("15890978905");
        user.setRegisterTime(new Date());
        user.setPopedom(1);
        users.add(user);

        user = new User();
        user.setId(4);
        user.setUsername("郑智化");
        user.setPassword("33333");
        user.setTelephone("15990956905");
        user.setRegisterTime(new Date());
        user.setPopedom(1);
        users.add(user);

        return users;
    }

}

接着创建UserController来访问页面

package com.yubotao.thymeleaf.controller;

import com.yubotao.thymeleaf.model.User;
import com.yubotao.thymeleaf.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 10:52 2018/9/7
 * @Modified By:
 */
@Controller
@RequestMapping(path = "/user")
public class UserController {

    @Autowired
    UserService userService;

    @RequestMapping("/showOneUser")
    public String showOneUser(Model model){
        User user = userService.findOneUser();
        model.addAttribute("user",user);

        return "showOneUser";
    }

    @RequestMapping("/showAllUsers")
    public String showAllUsers(Model model){
        List<User> userList = userService.findAllUsers();
        model.addAttribute("users", userList);

        return "showAllUsers";
    }

}

新建页面showOneUser.html

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8"/>
    <title>显示用户信息</title>
</head>
<body>
<h1>两种形式:</h1>
<h3>第一种:直接通过${}来取值</h3>

                    编号:<span th:text="${user.id}"></span><br/>
                    用户名:<span th:text="${user.username}"></span><br/>
                    密码:<span th:text="${user.password}"></span><br/>
                    电话:<span th:text="${user.telephone}"></span><br/>
                    注册时间:<span th:text="${#dates.format(user.registerTime, 'yyyy-MM-dd hh:mm:ss')}"></span><br/>
                    权限:<span th:text="${user.popedom==0?'管理员':'普通用户'}"></span><br/>
<hr>
<h3>第二种:先通过${}取到对象,然后再通过*{}来取值</h3>
                    <div th:object="${user}">
                        编号:<span th:text="*{id}"></span><br/>
                        用户名:<span th:text="*{username}"></span><br/>
                        密码:<span th:text="*{password}"></span><br/>
                        电话:<span th:text="*{telephone}"></span><br/>
                        注册时间:<span th:text="*{#dates.format(registerTime, 'yyyy-MM-dd hh:mm:ss')}"></span><br/>
                        权限:<span th:text="*{popedom==0?'管理员':'普通用户'}"></span><br/>
                    </div>

</body>
</html>

访问查看结果

这里写图片描述

接着创建showAllUsers.html

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8"/>
    <title>显示全部用户</title>

</head>
<body>
<div class="panel-body">
    <ul class="list-group">
        <li class="list-group-item" th:each="user:${users}">
            编号:<span th:text="${user.id}"></span><br/>
            用户名:<span th:text="${user.username}"></span><br/>
            密码:<span th:text="${user.password}"></span><br/>
            电话:<span th:text="${user.telephone}"></span><br/>
            注册时间:<span th:text="${#dates.format(user.registerTime, 'yyyy-MM-dd hh:mm:ss')}"></span><br/>
            权限:<span th:text="${user.popedom==0?'管理员':'普通用户'}"></span><br/>
        </li>
    </ul>
</div>
<hr>
<div class="col-md-7">
    <div class="panel panel-primary">
        <div class="panel-heading text-center">
            <span class="panel-title">全部用户信息</span>
        </div>
        <div class="panel-body">
            <table class="table-bordered" style="width: 100%">
                <tr style="height: 40px; background-color: #f7ecb5">
                    <th class="text-center">编号</th>
                    <th class="text-center">用户名</th>
                    <th class="text-center">密码</th>
                    <th class="text-center">电话</th>
                    <th class="text-center">注册时间</th>
                    <th class="text-center">权限</th>
                </tr>
                <tr th:each="user:${users}" class="text-center" style="height: 40px">
                    <td><span th:text="${user.id}"></span></td>
                    <td><span th:text="${user.username}"></span></td>
                    <td><span th:text="${user.password}"></span></td>
                    <td><span th:text="${user.telephone}"></span></td>
                    <td><span th:text="${#dates.format(user.registerTime, 'yyyy-MM-dd hh:mm:ss')}"></span></td>
                    <td><span th:text="${user.popedom==0?'管理员':'普通用户'}"></span></td>
                </tr>
            </table>
        </div>
    </div>
</div>

</body>
</html>

这里是两种样式,懒得改了,其实两种形式都一样,都是用了th:each来循环遍历。

这里写图片描述

一些表达式和变量的使用等

我们新增一个接口

@RequestMapping("test")
    public String test(Model model, HttpSession session, HttpServletRequest request, HttpServletResponse response){
        WebContext ctx = new WebContext(request, response, request.getServletContext());
        ctx.setVariable("book", "《智能时代》");
        session.setAttribute("city", "醉美泸州");

        return "testThymeleafObjects";
    }

接着新建一个页面testThymeleafObjects.html

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title>测试thymeleaf对象</title>
    <meta charset="UTF-8"/>
</head>
<body>
#ctx.#vars:<br/>
<textarea th:text="${#ctx.#vars}" rows="10" cols="150"></textarea><br/>
<!--这个#vars不好使-->
喜欢看的书:<span th:text="${#vars.book}"></span><br>
喜欢的城市:<span th:text="${#httpSession.getAttribute('city')}"></span><br>
你的国家:<span th:text="${#locale.country}+'-'+${#locale.getDisplayCountry()}"></span><br>
你的母语:<span th:text="${#locale.language}+'-'+${#locale.getDisplayLanguage()}"></span><br>
<hr>
时间:<span th:text="${#dates.format(#dates.createNow())}"></span><br>
收入:<span th:text="'¥' + ${#numbers.formatDecimal(2345.5645345, 3, 2)}"></span><br>
</body>
</html>

然后查看页面

这里写图片描述

这里有部分变量有问题,就是th:text="${#httpSession.getAttribute('city')}"这里,我暂时就没管了。

这些应该都是Thymeleaf内置的一些变量什么的,感兴趣的可以查查,网上这种资料一堆,再不济去看官网。

条件判断

接着我们在resources中新建application.yaml文件

server:
  port: 8080

serverHost:
  inetAddressA:
    ip: 127.0.0.1
    length: 160
    port: 2000
  inetAddressB:
    ip: 192.168.0.15
    length: 180
    port: 2000
  inetAddressC:
      ip: 192.168.0.16
      length: 288
      port: 2000

udp:
  server:
    host: 192.168.60.34
    port: 8001

并创建一个ServerHostProperties类来对应属性信息

package com.yubotao.thymeleaf.properties;

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


/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 19:16 2018/9/10
 * @Modified By:
 */
@Component
@ConfigurationProperties("serverhost")
public class ServerHostProperties {
    private InetAddress inetAddressA;
    private InetAddress inetAddressB;
    private InetAddress inetAddressC;

    public static class InetAddress{
        private String ip;
        private int length;
        private int port;

        public String getIp() {
            return ip;
        }

        public void setIp(String ip) {
            this.ip = ip;
        }

        public int getLength() {
            return length;
        }

        public void setLength(int length) {
            this.length = length;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }
    }

    public InetAddress getInetAddressA() {
        return inetAddressA;
    }

    public void setInetAddressA(InetAddress inetAddressA) {
        this.inetAddressA = inetAddressA;
    }

    public InetAddress getInetAddressB() {
        return inetAddressB;
    }

    public void setInetAddressB(InetAddress inetAddressB) {
        this.inetAddressB = inetAddressB;
    }

    public InetAddress getInetAddressC() {
        return inetAddressC;
    }

    public void setInetAddressC(InetAddress inetAddressC) {
        this.inetAddressC = inetAddressC;
    }
}

然后创建一个ServerHostController写相关接口

package com.yubotao.thymeleaf.controller;

import com.yubotao.thymeleaf.properties.ServerHostProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

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

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 19:18 2018/9/10
 * @Modified By:
 */
@Controller
@RequestMapping(path = "/server")
public class ServerHostController {

    @Autowired
    private ServerHostProperties serverHostProperties;

    @RequestMapping("/showServerHost")
    public String serverHost(Model model){
        List<ServerHostProperties.InetAddress> inetAddresses = new ArrayList<ServerHostProperties.InetAddress>();
        inetAddresses.add(serverHostProperties.getInetAddressA());
        inetAddresses.add(serverHostProperties.getInetAddressB());
        inetAddresses.add(serverHostProperties.getInetAddressC());
        model.addAttribute("inetAddresses", inetAddresses);

        return "showServerHost";
    }

}

最后我们写一个showServerHost.html页面,其中涉及了if判断

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title>显示服务器主机信息</title>
    <meta charset="UTF-8"/>
</head>
<body>
<div class="panel-heading text-center">
    <span class="panel-title">服务器主机信息</span>
</div>
<div class="panel-body">
    <ul class="list-group">
        <li class="list-group-item" th:each="inetAddress:${inetAddresses}">
            <div th:if="${inetAddressStat.count==1}">
                <p style="font-weight: bold">inetAddressA</p>
            </div>
            <div th:if="${inetAddressStat.count==2}">
                <p style="font-weight: bold">inetAddressB</p>
            </div>
            <div th:if="${inetAddressStat.count==3}">
                <p style="font-weight: bold">inetAddressC</p>
            </div>
            ip:<span th:text="${inetAddress.ip}"></span><br/>
            length:<span th:text="${inetAddress.length}"></span><br/>
            port:<span th:text="${inetAddress.port}"></span><br/>
        </li>
    </ul>
</div>
</body>
</html>

我们访问一下,看下效果

这里写图片描述

可以看到起效了。

这里顺便说一下inetAddressStat.count这个变量,查了一下,它表示的是循环遍历的list中,某个对象是该list中的第几个对象,应该是一个排序功能。

表单提交

最后,我们看下最关键的表单提交,这也是前后端交互很常用的。

首先我们新建一个AnimalForm

package com.yubotao.thymeleaf.model;



import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 9:55 2018/9/12
 * @Modified By:
 */
public class AnimalForm {

    @Setter
    @Getter
    private long id;

    @Setter
    @Getter
    @NotEmpty(message = "动物名:不能为空")
    private String oname;

    @Setter
    @Getter
    @Range(min = 1, message="数量:必须大于0")
    @NotNull(message = "数量:不能为空")
    private int ocount;

    @Setter
    @Getter
    @Size(max = 10, message = "备注:长度不能超过10个字符")
    private String memo;

}

然后新建一个ZooController

package com.yubotao.thymeleaf.controller;

import com.yubotao.thymeleaf.model.AnimalForm;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.Valid;

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 9:59 2018/9/12
 * @Modified By:
 */
@Controller
@RequestMapping(path = "/zoo")
public class ZooController {

    @RequestMapping(path = "list", method = RequestMethod.GET)
    public ModelAndView showZooList(){
        ModelAndView mav = new ModelAndView();
        mav.setViewName("zoolist");
        mav.addObject("animalForm", new AnimalForm());
        return mav;
    }

    @RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)
    public String doAdd(Model model, @Valid AnimalForm animalForm, BindingResult result){
        System.out.println("动物名: " + animalForm.getOname());
        System.out.println("数量: " + animalForm.getOcount());
        System.out.println("备注: " + animalForm.getMemo());
        if (result.hasErrors()){
            model.addAttribute("MSG", "出错啦!");
        }else {
            model.addAttribute("MSG", "提交成功!");
        }
        return "zoolist";
    }

}

最后新建一个zoolist.html页面

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Zoo List</title>
</head>
<body>
<a href=".">首页</a>->动物列表<br><br>

<div th:text="${MSG}">这里是信息提示</div>
<br>
<div th:errors="${animalForm.oname}"></div>
<div th:errors="${animalForm.ocount}"></div>
<div th:errors="${animalForm.memo}"></div>
<br>

<form id="iform" th:action="@{/zoo/list?save}" th:method="post" th:object="${animalForm}">
    <table border="1">
        <tr>
            <th>动物名称</th>
            <th>数量</th>
            <th>备注</th>
            <th>Action</th>
        </tr>
        <tr>
            <td><input type="text" name="oname" value="" th:value="*{oname}"/></td>
            <td><input type="text" name="ocount" value="" th:value="*{ocount}"/></td>
            <td><input type="text" name="memo" value="" th:value="*{memo}"/></td>
            <td><input type="submit" value="添加"/></td>
        </tr>
    </table>
</form>
<hr>
<table border="1">
    <tr>
        <th>序号</th>
        <th>动物名称</th>
        <th>数量</th>
        <th>备注</th>
    </tr>
    <tr>
        <td>1</td>
        <td>大马猴</td>
        <td>10</td>
        <td>机灵古怪,俏皮活泼</td>
    </tr>
    <tr>
        <td>2</td>
        <td>大熊猫</td>
        <td>80</td>
        <td>体型笨重,喜欢吃竹子</td>
    </tr>
    <tr>
        <td>3</td>
        <td>澳洲羊驼</td>
        <td>13</td>
        <td>长相奇特,大国人俗称其草泥马</td>
    </tr>
    <tr>
        <td>4</td>
        <td>峨眉山猴</td>
        <td>90</td>
        <td>不怕人,有时候发贱抢游客面包吃</td>
    </tr>
</table>

</body>
</html>

这页里面最关键的部分我给提取出来,就是

<form id="iform" th:action="@{/zoo/list?save}" th:method="post" th:object="${animalForm}">
    <table border="1">
        <tr>
            <th>动物名称</th>
            <th>数量</th>
            <th>备注</th>
            <th>Action</th>
        </tr>
        <tr>
            <td><input type="text" name="oname" value="" th:value="*{oname}"/></td>
            <td><input type="text" name="ocount" value="" th:value="*{ocount}"/></td>
            <td><input type="text" name="memo" value="" th:value="*{memo}"/></td>
            <td><input type="submit" value="添加"/></td>
        </tr>
    </table>
</form>

主要涉及了th:action:用来设置请求接口的url;th:method:用来设置请求方法;th:object:这里和前面get方法传过来的Object名对应。

@RequestMapping(path = "list", method = RequestMethod.GET)
    public ModelAndView showZooList(){
        ModelAndView mav = new ModelAndView();
        mav.setViewName("zoolist");
        mav.addObject("animalForm", new AnimalForm());
        return mav;
    }

功能很简单,首先get方法请求到这个页面,然后我们在这个页面发送数据,之后查看后台控制台输出是否正确。

首先请求到页面

这里写图片描述

然后我们填上数据并提交

这里写图片描述

接着看后台控制台输出是否一致

这里写图片描述

至此form表单提交也就完成了。

这里面涉及了AnimalForm中的几个注解,简单提一下:

  • @NotEmpty(message = "动物名:不能为空"):这个属性值不能为空,否则会报错。报错信息在html文件中有写
<div th:errors="${animalForm.oname}"></div>

效果如下:

这里写图片描述

通过这些报错信息,大概能猜出那几个注解的作用。

  • @Range(min = 1, message="数量:必须大于0"):控制大小,从1开始,所以就必须大于0
  • @Size(max = 10, message = "备注:长度不能超过10个字符"):规定了最大值为10,所以长度不能超。

最后是ZooController中的post方法,里面有个params = {"save"},并且我们看到在form表单中的th:action="@{/zoo/list?save}",这里表示只对含有save参数的url请求起作用,如果发现客户端传递来的参数里没有save则不应答,对于这个方法,请求http://localhost:7777/zoo/list?savehttp://localhost:7777/zoo/list?save=222都会应答。

@Valid表示要对该form进行验证,spring框架会根据字段名称将页面传递过来的值绑定到animalForm中。


到此我的学习就暂时告一段落,如果后续需要更加深入的学习,或者有新的新的体会,应该会开一篇新的文章。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值