Jason

Jason 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

以终为始,闭环迭代,持续提高。

个人动态

Jason 发布了文章 · 10月19日

15-基于Spring,MyBatis,SpringBoot,Vue技术实现购物券系统的增删改查操作。

业务描述

基于Spring,MyBatis,SpringBoot,VUE技术实现购物券的增删改查操作。

项目环境初始化

准备工作

1. MySQL(5.7)
2. JDK (1.8)
3. Maven (3.6.3)
4. IDEA(2020.2)

数据库初始化

打开mysql控制台,然后按如下步骤执行dbretail.sql文件。
第一步:登录mysql。

mysql –uroot –proot

第二步:设置控制台编码方式。

set names utf8;

第三步:执行dbretail.sql文件(切记不要打开文件复制到mysql客户端运行)。

source d:/dbretail.sql

其中dbvoucher.sql文件内容如下:

drop database if exists dbretail;
create database dbretail default character set utf8;
use dbretail;
CREATE TABLE tb_voucher(
     `id`  INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
     `deno` DECIMAL(10,2) UNSIGNED NOT NULL COMMENT '面值',
     `requirement` DECIMAL(10,2) UNSIGNED NOT NULL COMMENT '订单多少钱可以使用',
     `startTime`  DATETIME  COMMENT '起始日期',
     `endTime`  DATETIME  COMMENT '截至日期',
     `maxNum` INT COMMENT '代金卷发放最大数量',
     `remark` varchar(200) COMMENT '备注',
    `createdTime`  DATETIME  COMMENT '创建日期'
) COMMENT='购物券表';
insert into tb_voucher values (null,100,1000,'2020/05/07','2020/06/07',200,'Remark',now());
insert into tb_voucher values (null,80,800,'2020/05/07','2020/06/07',300,'Remark',now());
insert into tb_voucher values (null,50,500,'2020/05/07','2020/06/07',400,'Remark',now());
insert into tb_voucher values (null,30,300,'2020/05/07','2020/06/07',500,'Remark',now());
insert into tb_voucher values (null,10,100,'2020/05/07','2020/06/07',600,'Remark',now());

创建项目并添加依赖

基于IDEA创建

第一步:创建Empty项目并设置基本信息(假如Empty项目已经存在则无需创建)

image.png

image.png

第二步:基于start.spring.io 创建项目Module,并设置基本信息。

image.png

image.png

第三步:创建项目module时指定项目核心依赖

image.png

第四步:继续完成module的创建并分析其结构

image.png

项目配置文件内容初始化

#server
server.port=80
#spring datasource
spring.datasource.url=jdbc:mysql:///dbvoucher?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

#spring mybatis
mybatis.mapper-locations=classpath:/mapper/*/*.xml

#spring logging
logging.level.com.cy.retail=debug

项目API架构设计

其API架构设计,如图所示:

image.png

商品查询业务实现

业务描述

从数据库查询购物卷信息,并将购物卷信息呈现在页面上,如图所示:

image.png

业务时序分析

查询所有购物券信息,其业务时序分析,如图所示:
image.png

Pojo类定义

定义Voucher对象,用于封装从数据库查询到的购物券信息。

package com.cy.retail.voucher.pojo;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;
/**购物卷*/
public class Voucher implements Serializable {
    private static final long serialVersionUID = -8863293149012534747L;
    private Integer id;
    /**面值*/
 private Integer deno;
    /**条件(多少钱可以使用)*/
 private Integer requirement;
    /**起始时间*/
 @JsonFormat(pattern = "yyyy/MM/dd HH:mm")
    private Date startTime;
    /**结束时间*/
 @JsonFormat(pattern = "yyyy/MM/dd HH:mm")
    private Date endTime;
    /**代金卷发放最大数量*/
 private Integer maxNum;
    /**备注*/
 private String remark;
    /**创建时间*/
 private Date createdTime;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getDeno() {
        return deno;
    }
    public void setDeno(Integer deno) {
        this.deno = deno;
    }
    public Integer getRequirement() {
        return requirement;
    }
    public void setRequirement(Integer requirement) {
        this.requirement = requirement;
    }
    public Date getStartTime() {
        return startTime;
    }
    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }
    public Date getEndTime() {
        return endTime;
    }
    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }
    public Integer getMaxNum() {
        return maxNum;
    }
    public void setMaxNum(Integer maxNum) {
        this.maxNum = maxNum;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
    public Date getCreatedTime() {
        return createdTime;
    }
    public void setCreatedTime(Date createdTime) {
        this.createdTime = createdTime;
    }
    @Override
 public String toString() {
        return "Voucher{" +
                "id=" + id +
                ", deno=" + deno +
                ", requirement=" + requirement +
                ", startTime=" + startTime +
                ", endTime=" + endTime +
                ", maxNum=" + maxNum +
                ", remark='" + remark + ''' +
                ", createdTime=" + createdTime +
                '}';
    }
}

Dao接口方法及映射定义

在GoodsDao接口中定义商品查询方法以及SQL映射,基于此方法及SQL映射获取所有商品信息。代码如下:

package com.cy.retail.voucher.dao;
import com.cy.retail.voucher.pojo.Voucher;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface VoucherDao {

    @Select("select count(*) from tb_voucher")
    int getRowCount();
    @Select("select * from tb_voucher order by createdTime desc limit #{startIndex},#{pageSize}")
    List<Voucher> findPageObjects(Integer startIndex, Integer pageSize);
}

编写单元测试类进行测试分析:

package com.cy.retail.voucher.dao;
import com.cy.retail.voucher.pojo.Voucher;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class VoucherDaoTests {
    @Autowired
    private VoucherDao voucherDao;
    @Test
    void testGetRowCount(){
        int rowCount=voucherDao.getRowCount();
        System.out.println("rowCount="+rowCount);
    }
    @Test
    void testFindPageObjects(){
        List<Voucher> list=voucherDao.findPageObjects(0,3);
        list.forEach(item-> System.out.println(item));
    }
}

Service接口方法定义及实现

业务逻辑层分页逻辑封装对象定义,代码如下:

package com.cy.retail.common.pojo;
import java.io.Serializable;
import java.util.List;
public class PageObject<T> implements Serializable {
    /**总记录数*/
 private Integer rowCount;
    /**当前页记录*/
 private List<T> records;
    /**总页数(计算出来)*/
 private Integer pageCount;
    /**当前页码值*/
 private Integer pageCurrent;
    /**页面大小(每页最多显示多少条记录)*/
 private Integer pageSize;
    public PageObject(Integer rowCount, List<T> records, Integer pageCurrent, Integer pageSize) {
        this.rowCount = rowCount;
        this.records = records;
        this.pageCurrent = pageCurrent;
        this.pageSize = pageSize;
        this.pageCount=rowCount/pageSize;
        if(this.rowCount%this.pageSize!=0)this.pageCount++;
    }
    public Integer getRowCount() {
        return rowCount;
    }
    public void setRowCount(Integer rowCount) {
        this.rowCount = rowCount;
    }
    public List<T> getRecords() {
        return records;
    }
    public void setRecords(List<T> records) {
        this.records = records;
    }
    public Integer getPageCount() {
        return pageCount;
    }
    public void setPageCount(Integer pageCount) {
        this.pageCount = pageCount;
    }
    public Integer getPageCurrent() {
        return pageCurrent;
    }
    public void setPageCurrent(Integer pageCurrent) {
        this.pageCurrent = pageCurrent;
    }
    public Integer getPageSize() {
        return pageSize;
    }
    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }
    @Override
 public String toString() {
        return "PageObject{" +
                "rowCount=" + rowCount +
                ", records=" + records +
                ", pageCount=" + pageCount +
                ", pageCurrent=" + pageCurrent +
                ", pageSize=" + pageSize +
                '}';
    }
}

VoucherService接口及查询方法定义

package com.cy.retail.voucher.service;
import com.cy.retail.common.pojo.PageObject;
import com.cy.retail.voucher.pojo.Voucher;
public interface VoucherService {
    /**
 * 分页查询Voucher信息
 * @param pageCurrent
 * @param pageSize
 * @return
 */ PageObject<Voucher> findPageObjects(Integer pageCurrent,Integer pageSize);
}

VoucherService接口实现类VoucherServiceImpl定义及方法实现

package com.cy.retail.voucher.service.impl;
import com.cy.retail.common.pojo.PageObject;
import com.cy.retail.voucher.dao.VoucherDao;
import com.cy.retail.voucher.pojo.Voucher;
import com.cy.retail.voucher.service.VoucherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class VoucherServiceImpl implements VoucherService {
    @Autowired
 private VoucherDao voucherDao;
    @Override
 public int deleteById(Integer id) {
        return voucherDao.deleteById(id);
    }
    @Override
 public PageObject<Voucher> findPageObjects(Integer pageCurrent, Integer pageSize) {
        int rowCount=voucherDao.getRowCount();
        int startIndex=(pageCurrent-1)*pageSize;
        List<Voucher> records=voucherDao.findPageObjects(startIndex,pageSize);
        return new PageObject<>(rowCount,records,pageCurrent,pageSize);
    }
}

编写单元测试类进行测试分析

package com.cy.retail.voucher.service;
import com.cy.retail.common.pojo.PageObject;
import com.cy.retail.voucher.dao.VoucherDao;
import com.cy.retail.voucher.pojo.Voucher;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class VoucherServiceTests {
    @Autowired
 private VoucherService voucherService;
    @Test
 void testFindPageObjects(){
        PageObject<Voucher> pageObject=
        voucherService.findPageObjects(1,3);
        System.out.println(pageObject);
    }
}

Controller对象方法定义及实现

控制逻辑层响应数据标准对象设计及实现,代码如下:

package com.cy.retail.common.pojo;
import java.io.Serializable;
public class ResponseResult implements Serializable {
    private static final long serialVersionUID = -4971076199594828397L;
    /**状态:1 ok,0 error*/
 private Integer state=1;
    /**状态信息*/
 private String message="ok";
    /**业务层返回的正确响应数据*/
 private Object data;
    public ResponseResult(){}
    public ResponseResult(String message){ //new JsonResult("update ok");
 this.message=message;
    }
    public ResponseResult(Object data){
        this.data=data;
    }
    public ResponseResult(Throwable e){
        this.state=0;
        this.message=e.getMessage();
    }
    public Integer getState() {
        return state;
    }
    public void setState(Integer state) {
        this.state = state;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
}

定义VoucherController类,并添加doFindPageObjects方法,代码如下:

ackage com.cy.retail.voucher.controller;
import com.cy.retail.common.pojo.PageObject;
import com.cy.retail.common.pojo.ResponseResult;
import com.cy.retail.voucher.service.VoucherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class VoucherController {
    @Autowired
 private VoucherService voucherService;
    @GetMapping("/voucher/doFindPageObjects/{pageCurrent}/{pageSize}")
    public ResponseResult doFindPageObjects(@PathVariable Integer pageCurrent,@PathVariable Integer pageSize){
        return new ResponseResult(voucherService.findPageObjects(pageCurrent,pageSize));
    }
}

控制层统一异常处理类设计及实现,关键代码如下:

package com.cy.retail.common.web;
import com.cy.retail.common.pojo.ResponseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @ExceptionHandler(RuntimeException.class)
    public ResponseResult doHandleRuntimeException(RuntimeException e){
        e.printStackTrace();//控制台输出
 log.error("exception msg {}"+e.getMessage());
        return new ResponseResult(e);
    }
}

Voucher页面设计及实现

在static目录中添加voucher.html页面,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div id="app" class="container">
    <h1>The Voucher Page</h1>
       <table class="table">
         <thead>
          <tr>
            <th>序号</th>
            <th>面值</th>
            <th>可用条件</th>
            <th>起始日期</th>
            <th>截止日期</th>
            <th>数量</th>
            <th>操作</th>
         </tr>
        </thead>
           <tbody>
           <tr v-for="(v,i) of vouchers" :key="i">
                <td>{{i+1}}</td>
                <td>{{v.deno}}</td>
               <td>{{v.requirement}}</td>
               <td>{{v.startTime}}</td>
               <td>{{v.endTime}}</td>
               <td>{{v.maxNum}}</td>
               <td><button class="btn btn-danger btn-xs">delete</button></td>
           </tr>
        </tbody>
    </table>
    <div id="pageId" class="box-footer clearfix" dm="100">
        <ul class="pagination pagination-sm no-margin pull-right">
            <li><a class="first" @click="doJumpToPage">首页</a></li>
            <li><a class="pre" @click="doJumpToPage">上一页</a></li>
            <li><a class="next" @click="doJumpToPage">下一页</a></li>
            <li><a class="last" @click="doJumpToPage">尾页</a></li>
            <li><a class="rowCount">总记录数({{rowCount}})</a></li>
            <li><a class="pageCount">总页数({{pageCount}})</a></li>
            <li><a class="pageCurrent">当前页({{pageCurrent}})</a></li>
        </ul>
    </div>
</div>
<script data-original="/js/axios.min.js"></script>
<script data-original="/js/vue.js"></script>
<script>
    var vm=new Vue({
        el:"#app",
        data:{
          vouchers:{},
          pageCurrent:1,
          pageCount:0,
          rowCount:0
 },
        methods:{
           doFindPageObjects(){
               let url=`voucher/doFindPageObjects/${this.pageCurrent}/3`;
               axios.get(url).then(function(result){
                   let respData=result.data.data;
                   this.vm.vouchers=respData.records;
                   this.vm.rowCount=respData.rowCount;
                   this.vm.pageCount=respData.pageCount;
                   this.vm.pageCurrent=respData.pageCurrent;
               })
           },
           doJumpToPage(event){
               let cls=event.target.getAttribute("class");
               if(cls=="first"){
                   this.pageCurrent=1;
               }else if(cls=="pre"&& this.pageCurrent>1){
                   this.pageCurrent--;
               }else if(cls=="next"&& this.pageCurrent< this.pageCount){
                   this.pageCurrent++;
               }else if(cls=="last"){
                   this.pageCurrent= this.pageCount;
               }else{
                   return;
               }
               //3.基于新的页码值重新执行查询
 this.doFindPageObjects();
           }
        },
        mounted:function(){
             this.doFindPageObjects();
        }
    });
</script>
</body>
</html>

启动服务器进行访问测试分析

首先,启动tomcat,然后在打开浏览器,在地址栏输入访问地址,获取服务端数据并进行呈现,如图所示:
image.png

购物券删除业务实现

业务描述

从数据库查询购物券信息后,点击页面上删除按钮,基于id删除当前行记录,如图所示:

image.png

业务时序分析

在购物券呈现页面,用户执行删除操作,其删除时序如图所示:

image.png

Dao接口方法及映射定义

在VoucherDao接口中定义删除方法以及SQL映射,代码如下:

 @Delete("delete from tb_voucher where id=#{id}")

 int deleteById(Integer id);

Service接口方法定义及实现

在VoucherService接口中添加删除方法,代码如下:

int deleteById(Integer id);

在VoucherService的实现类VoucherServiceImpl中添加deleteById方法实现。代码如下。

@Override
    public int deleteById(Integer id) {
        int rows=voucherDao.deleteById(id);

        return rows;
    }

Controller对象方法定义及实现

在VoucherController中的添加doDeleteById方法,代码如下:

 @DeleteMapping("/voucher/doDeleteById/{id}")
public ResponseResult doDeleteById(@PathVariable Integer id){
    voucherService.deleteById(id);
    return new ResponseResult(id);
}

说明:Restful 风格为一种软件架构编码风格,定义了一种url的格式,其url语法为/a/b/{c}/{d},在这样的语法结构中{}为一个变量表达式。假如我们希望在方法参数中获取rest url中变量表达式的值,可以使用@PathVariable注解对参数进行描述。

Voucher页面上删除按钮事件处理

在voucher.html页面中添加删除事件,代码如下:

<button @click="doDeleteById(v.id)" class="btn btn-danger btn-xs">delete</button>

在vue的methods对象中添加删除方法,代码如下:

doDeleteById(id){
    let url=`voucher/doDeleteById/${id}`;
    axios.delete(url).then(function(result){
        alert(result.data.message);
        this.vm.doFindPageObjects();
    })
}

启动tomcat服务器进行访问测试分析

启动tomcat,然后在打开浏览器,在地址栏输入访问地址,获取服务端数据并进行呈现,接下来点击页面上的删除按钮进行测试。

购物券添加业务设计实现

业务描述

在购物券页面中设计添加表单,然后点击表单中的Save 按钮时将表单数据异步提交到服务端进行保存,如图所示:

image.png

业务时序设计及分析

在购物券页面,输入购物券信息,然后提交到服务端进行保存,其时序分析如图所示:

DAO接口方法设计及实现

在VoucherDao中添加用于保存商品信息的接口方法,代码如下:

int insertObject(Voucher entity);

在VoucherMapper文件中添加insertObject方法对应的SQL映射,代码如下:

<?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.cy.retail.voucher.dao.VoucherDao">
    <insert id="insertObject">
        insert into tb_voucher
        (deno,requirement,startTime,endTime,maxNum,remark,createdTime)
        values
        (#{deno},#{requirement},#{startTime},#{endTime},#{maxNum},#{remark},now())
    </insert>
</mapper>

Service接口方法设计及实现

在VoucherService接口中添加业务方法,用于实现购物券信息添加,代码如下:

int saveObject(Voucher entity);

在VoucherSerivceImpl类中添加接口方法实现,代码如下:

@Override
public int saveObject(Voucher entity) {
    return voucherDao.insertObject(entity);
}

Controller类中方法设计及实现

在VoucherController类中添加用于处理添加请求的方法,代码如下:

@PostMapping("/voucher/doSaveObject")
public ResponseResult doSaveObject(@RequestBody Voucher entity){
    System.out.println(entity);
    voucherService.saveObject(entity);
    return new ResponseResult("save ok");
}

购物券页面表单元素设计及事件处理

在voucher.html页面代码中设计添加表单,代码如下:

<div>
    <form>
        <input type="hidden" v-model="id">
        <div class="form-group">
            <label for="denoId">面值</label>
            <input type="text" v-model="deno" class="form-control" id="denoId" placeholder="面值">
        </div>
        <div class="form-group">
            <label for="requirementId">应用条件</label>
            <input type="text" v-model="requirement" class="form-control" id="requirementId" placeholder="应用条件">
        </div>
        <div class="form-group">
            <label for="startTimeId">起始日期</label>
            <input type="text" v-model="startTime" class="form-control" id="startTimeId" placeholder="起始日期">
        </div>
        <div class="form-group">
            <label for="endTimeId">截至日期</label>
            <input type="text" v-model="endTime" class="form-control" id="endTimeId" placeholder="截至日期">
        </div>
        <div class="form-group">
            <label for="maxNumId">最大数量</label>
            <input type="text" v-model="maxNum" class="form-control" id="maxNumId" placeholder="最大数量">
        </div>
        <div class="form-group">
            <label for="maxNumId">备注</label>
            <textarea v-model="remark" class="form-control" id="remarkId" placeholder="备注"></textarea>
        </div>
        <div class="form-group">
            <input type="button" @click="doSaveOrUpdate" value="Save Data" class="btn btn-primary"/>
        </div>
    </form>
</div>

在Vue对象的Data区添加属性数据,代码如下:

deno:"",
requirement:"",
startTime:"",
endTime:"",
maxNum:"",
remark:"",

在Vue对象中的Methods区,添加保存按钮事件处理函数,代码如下:
doSaveOrUpdate(){

    let url="voucher/doSaveObject";
    let params={"id":this.id,"deno":this.deno,"requirement":this.requirement,"startTime":this.startTime,"endTime":this.endTime,"maxNum":this.maxNum,"remark":this.remark};
    console.log(params);
    axios.post(url,params).then(function(result){
        alert(result.data.message);
        this.vm.doFindPageObjects();
        this.vm.id="";
        this.vm.deno="";
        this.vm.requirement="";
        this.vm.startTime="";
        this.vm.endTime="";
        this.vm.maxNum="";
        this.vm.remark="";
    })
}

购物券修改业务设计实现

业务描述

在购物券页面中设计修改按钮,并在点击修改按钮时候基于id查询购物券信息,然后呈现在表单中,如图所示:

image.png

业务时序设计及分析

基于用户更新设计其执行时序。

Dao接口方法设计及实现

在VoucherDao中添加基于id查询和更新的方法,代码如下:

@Select("select * from tb_voucher where id=#{id}")
Voucher findById(Integer id);
int updateObject(Voucher entity);

将updateObject方法的SQL映射添加到映射文件(建议SQL内容比较多的写到映射文件),代码如下:

<update id="updateObject">
    update tb_voucher
    set
     deno=#{deno},
     requirement=#{requirement},
     startTime=#{startTime},
     endTime=#{endTime},
     maxNum=#{maxNum},
     remark=#{remark}
    where id=#{id}
</update>

Service接口方法设计及实现

在VoucherService中添加基于id查询和更新的方法,代码如下:

Voucher findById(Integer id);
int updateObject(Voucher entity);

在VoucherServiceImpl类中添加基于id查询和更新的方法,代码如下:

@Override
public Voucher findById(Integer id) {
    return voucherDao.findById(id);
}
@Override
public int updateObject(Voucher entity) {
    return voucherDao.updateObject(entity);
}

Controller类中方法设计及实现

在VoucherController类中添加处理基于id的查询请求方法和更新购物券信息的方法,代码如下:

@GetMapping("/voucher/doFindById/{id}")
public ResponseResult doFindById(@PathVariable Integer id){
    Voucher voucher=voucherService.findById(id);
    return new ResponseResult(voucher);
}
@PostMapping("/voucher/doUpdateObject")
public ResponseResult doUpdateObject(@RequestBody Voucher entity){
    voucherService.updateObject(entity);
    return new ResponseResult("update ok");
}

Voucher页面中更新设计设计及实现

在voucher页面设计中,在vue对象中的methods区添加基于id的查询请求方法,代码如下:

doFindById(id){
    let url=`voucher/doFindById/${id}`;
    axios.get(url).then(function(result){
        console.log(result.data);
        this.vm.id=result.data.data.id;
        this.vm.deno=result.data.data.deno;
        this.vm.requirement=result.data.data.requirement;
        this.vm.startTime=result.data.data.startTime;
        this.vm.endTime=result.data.data.endTime;
        this.vm.maxNum=result.data.data.maxNum;
        this.vm.remark=result.data.data.remark;
    })
}

在voucher页面设计中,在vue对象中的methods区修改doSaveOrUpdate方法,代码如下:

doSaveOrUpdate(){
    let url=this.id?"voucher/doUpdateObject":"voucher/doSaveObject";
    let params={"id":this.id,"deno":this.deno,"requirement":this.requirement,"startTime":this.startTime,"endTime":this.endTime,"maxNum":this.maxNum,"remark":this.remark};
    console.log(params);
    axios.post(url,params).then(function(result){
        alert(result.data.message);
        this.vm.doFindPageObjects();
        this.vm.id="";
        this.vm.deno="";
        this.vm.requirement="";
        this.vm.startTime="";
        this.vm.endTime="";
        this.vm.maxNum="";
        this.vm.remark="";
    })
}

启动服务器进行访问测试分析

总结(Summary)

本小节重点讲解了SpringBoot工程下MyBatis,SpringMVC,Vue技术的综合应用,重点理解其业务实现过程以及问题的解决过程。

查看原文

赞 20 收藏 7 评论 0

Jason 发布了文章 · 10月18日

15-基于Spring,MyBatis,SpringBoot,Vue技术实现购物券的增删改查操作。

基于Spring,MyBatis,SpringBoot,Vue技术实现购物券的增删改查操作

查看原文

赞 4 收藏 3 评论 0

Jason 发布了文章 · 10月16日

15-SpringBoot工程中前端JS问题调试分析

项目中客户端JS常见问题

image.png

image.png

image.png

image.png

image.png

image.png

image.png

总结(Summary)

本小节主要是列出了几个客户端常见问题,这些问题的解决要结合业务进行断点,日志,排除法进行分析,然后从实践中进行提高.

查看原文

赞 5 收藏 3 评论 0

Jason 发布了文章 · 10月14日

14-SpringBoot+Mybatis+Vue 实现商品模块的crud操作

准备工作

第一步 创建新module,名字为10-springboot-goods-vue.
第二步 添加maven依赖并进行初步配置(拷贝即可)
第三步 拷贝pojo,dao,service包中的所有接口和类.
第四步 拷贝静态资源到static目录(例如vue.js,axios.min.js)

商品查询设计及实现

创建GoodsController并定义相关方法,代码如下:

package com.cy.pj.goods.controller;
import com.cy.pj.goods.pojo.Goods;
import com.cy.pj.goods.service.GoodsService;
import java.util.List;
@RestController
public class GoodsController {
     @Autowired
     private GoodsService goodsService;
      /**查询所有商品信息*/
     @GetMapping("/goods/doFindGoods")
      public List<Goods> doFindGoods(){
          return goodsService.findGoods();
      }
}

在项目static目录创建goods-vue.html,并基于vue呈现数据,代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <div id="app">
      <h1>The Goods Page</h1>
      <table>
          <thead>
             <tr>
                 <th>id</th>
                 <th>name</th>
                 <th>remark</th>
                 <th>createdTime</th>
             </tr>
          </thead>
          <tbody>
             <tr v-for="g in goods">
                 <td>{{g.id}}</td>
                 <td>{{g.name}}</td>
                 <td>{{g.remark}}</td>
                 <td>{{g.createdTime}}</td>
             </tr>
          </tbody>
      </table>
  </div>
  <script data-original="js/axios.min.js"></script>
  <script data-original="js/vue.js"></script>
  <script>
    var vm=new Vue({//vue对象时vue.js应用的入口对象
             el:"#app",//vue对象要监控的区域
             data:{//此对象用于同步页面数据的一个对象
             goods:{}
             },
             methods:{//同步与页面元素事件处理函数
                doFindGoods:function(){
                 let url="goods/doFindGoods";
                 axios.get(url)
                      .then(function(result){
                            this.vm.goods=result.data;
                       });
                 }
             },
             mounted:function(){
                 this.doFindGoods();
             }
      });
  </script>
</body>
</html>

启动tomcat进行访问测试,如图所示:
image.png

商品删除设计及实现

在控制层方法中添加,处理删除逻辑的方法,代码如如下:

@RequestMapping("/goods/doDeleteById/{id}")
public String doDeleteById(@PathVariable("id")  Integer id){
    System.out.println("delete id "+id);
    goodsService.deleteById(id);
    return "delete ok";
}

在商品列表中的tr对象内部添加删除元素,代码如下:

<td><a @click="doDeleteById(g.id)">删除</a></td>

在商品模块的vue对象中添加执行删除逻辑的方法,代码如下:

doDeleteById:function(id){
    var url="goods/doDeleteById/"+id;
    axios.get(url)
        .then(function(res){
            alert(res.data);
            this.vm.doFindGoods();
        })
}

启动服务进行访问测试,检测其结果。

商品添加设计及实现

在Controller类中添加用于处理商品添加逻辑的方法,代码如下:

@RequestMapping("/goods/doSaveGoods")
public String doSaveGoods(@RequestBody Goods entity){
    System.out.println("entity="+entity);
    goodsService.saveGoods(entity);
    return "save ok";
}

在Goods页面上添加表单元素,用于实现用户输入,代码如下:

<form>
    <ul>
        <li>name</li>
        <li><input v-model="name"></li>
        <li>remark</li>
        <li><textarea v-model="remark"></textarea></li>
        <li><input type="button" @click="doSaveOrUpdateGoods" value="Save Goods"></li>
    </ul>
</form>

在vue对象内部添加用于同步表单数据的Data属性内容,代码如下:

data:{
    name:"",
    remark:"",
    goods:"",
}

在vue对象内部添加处理添加操作的事件处理函数,代码如下:

doSaveOrUpdateGoods:function(){
    var params={"name":this.name,"remark":this.remark};
    var url="goods/doSaveGoods";
    axios.post(url,params)
        .then(function(res){
            alert(res.data);
            this.vm.doFindGoods();
            this.vm.name="";
            this.vm.remark="";
        });
}

启动服务,进行添加操作测试。

商品修改设计及实现

在Controller中添加基于商品id查询和更新商品信息的方法,代码如下:

@RequestMapping("/goods/doFindById/{id}")
public Goods doFindById(@PathVariable("id") Integer id){
    return goodsService.findById(id);
}
@RequestMapping("goods/doUpdateGoods")
public String doUpdateGoods(@RequestBody Goods entity){
    goodsService.updateGoods(entity);
    return "update ok";
}

在Goods页面表单中添加隐藏的表单元素用于记录id值,代码如下:

<li><input type="hidden" v-model="id"></li>

在Goods页面记录中添加修改操作的需要的a元素,代码如下:

<td><a @click="doFindById(g.id)">修改</a></td>

在Vue对象中添加基于id查询的方法,代码如下:

doFindById:function(id){
    var url="goods/doFindById/"+id;
    axios.get(url)
    .then(function(res){
        console.log(res.data);
        this.vm.id=res.data.id;
        this.vm.name=res.data.name;
        this.vm.remark=res.data.remark;
    })
}

修改Vue对象中的用于保存和修改数据的方法,代码如下:

doSaveOrUpdateGoods:function(){
    var params={"id":this.id,"name":this.name,"remark":this.remark};
    var url=this.id?"goods/doUpdateGoods":"goods/doSaveGoods";
    axios.post(url,params)
        .then(function(res){
            this.vm.doFindGoods();
            alert(res.data);
            this.vm.id="";
            this.vm.name="";
            this.vm.remark="";
        });
}

启动服务进行访问测试,检测其结果。

总结(Summary)

本小节主要基于vue和axio技术实现了商品模块的基本操作,重点掌握客户端与服务端的交互和传值过程。

查看原文

赞 21 收藏 6 评论 0

Jason 发布了文章 · 10月12日

12-SpringBoot 工程中的响应标准设计及实现

背景分析

在基于C/S架构的编程模型中,客户端往往需要对服务端返回的数据,基于状态的不同进行不同的处理。例如,正确的状态数据一种呈现方式,错误的状态数据是另外一种呈现方式。于是服务端响应数据的标准化设计油然而生。

响应标准设计

在响应数据标准化设计时,首先要对响应数据进行分析,哪些数据要响应到客户端,对这些数据进行怎样的状态设计等。假如现在响应的业务数据包含三部分:状态,消息,具体数据。我们可以这样设计,例如:

package com.cy.pj.common.pojo;

/**
 * 基于此对象封装服务端响应到客户端的数据
 */
public class ResponseResult {
    /**响应状态码(有的人用code)*/
    private Integer state=1;//1表示ok,0表示error,.....
    /**状态码对应的信息*/
    private String message="ok";
    /**正确的响应数据*/
    private Object data;

    public ResponseResult(){}

    public ResponseResult(String message){//new ResponseResult("delete ok"),
        this.message=message;
    }
    public ResponseResult(Object data){//new ResponseResult(list);
        this.data=data;
    }
    public ResponseResult(Throwable e){//new ResponseResult(e);
        this.state=0;
        this.message=e.getMessage();
    }

    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

响应数据的封装

在Controller类的逻辑方法中进行正常的响应数据封装,例如:

package com.cy.pj.module.controller;

import com.cy.pj.common.pojo.ResponseResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ArithmeticController {

      @RequestMapping("/doCompute/{n1}/{n2}")
      public ResponseResult doCompute(@PathVariable  Integer n1, @PathVariable Integer n2){
          Integer result=n1/n2;
          ResponseResult r=new ResponseResult("计算结果:"+result);
          r.setData(result);
          return r;
      }
}

在全局异常处理对象中进行异常响应数据的封装,例如:

package com.cy.pj.common.web;

import com.cy.pj.common.pojo.ResponseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


@RestControllerAdvice
public class GlobalExceptionHandler {
      private static final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class);//2
      @ExceptionHandler(ArithmeticException.class)
      public ResponseResult doHandleArithmeticException(ArithmeticException e){
          e.printStackTrace();
          log.info("exception {}",e.getMessage());
          return new ResponseResult(e);//封装异常结果
      }
}

总结(Summary)

本小节主要讲解了Spring Boot工程中对逻辑数据的响应如何进行标准化设计,为什么这样设计,这样设计的好处以及如何设计。通过对这一小节的学习要提高其响应规范维度的设计理念,掌握基本技能。

查看原文

赞 15 收藏 6 评论 0

Jason 发布了文章 · 10月12日

11-SpringBoot 工程中的异常处理方式

背景分析

在项目的开发中,不管是对底层的数据逻辑操作过程,还是业务逻辑的处理过程,还是控制逻辑的处理过程,都不可避免会遇到各种可预知的、不可预知的异常。处理好异常对系统有很好的保护作用,同时会大大提高用户的体验。

异常处理分析

概述

Java项目中处理异常方式无非两种,要么执行trycatch操作,要么执行throw操作(抛给其它对象处理),无论采用哪种方式,其目的是让我们的系统对异常要有反馈。但现在的问题是我们如何让这种反馈代码的编写即简单又直观、友好。

处理规范

我们在处理异常的过程中通常要遵循一定的设计规范,例如:

  • 捕获异常时与抛出的异常必须完全匹配,或者捕获异常是抛出异常的父类类型。
  • 避免直接抛出RuntimeException,更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常(例如ServiceException)。
  • 捕获异常后必须进行处理(例如记录日志)。如果不想处理它,需要将异常抛给它的调用者。
  • 最外层的逻辑必须处理异常,将其转化成用户可以理解的内容。
  • 避免出现重复的代码(Don’t Repeat Yourself),即DAY原则。

SpringBoot 工程下的异常处理

准备工作

第一步:创建项目或module,并添加web依赖,代码如下:

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

第二步:修改项目访问端口为80,例如

server.port=80

第三步:定义Controller类,代码如下:

package com.cy.pj.arithmetic.controller;

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

@Controller
public class ArithmeticController {

  @RequestMapping("doCompute/{n1}/{n2}")
  @ResponseBody
  public String doCompute(@PathVariable  Integer n1, 
  @PathVariable Integer n2){
          Integer result=n1/n2;
          return "Result is "+result;
  }
}

第四步启动项目进行访问测试

在浏览器地址栏输入http://localhost/doCompute/10/2,检测输出结果。

Result is 5

默认异常处理

在浏览器地址栏输入http://localhost/doCompute/10/0,检测输出结果。

image.png

对于这样的默认异常处理(spring boot提供),用户体验不太友好,为了呈现更加友好的异常信息,我们通常要对异常进行自定义处理。

自己try异常处理

在控制层方法中,我们可以进行try catch处理,例如:

  @RequestMapping("doCompute/{n1}/{n2}")
  @ResponseBody
  public String doCompute(@PathVariable  Integer n1, 
  @PathVariable Integer n2){
          try{
          Integer result=n1/n2;
          return "Result is "+result;
          }catch(ArithmeticException e){
          return "exception is "+e.getMessage();
          }
  }
  

一个Controller类中通常会有多个方法,这样多个方法中都写try语句进行异常处理会带来大量重复代码的编写,不易维护。

Controller内部定义异常处理方法

在Controller类中添加异常处理方法,代码如下:

@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public String doHandleArithmeticException(ArithmeticException e){
    e.printStackTrace();
    return "计算过程中出现了异常,异常信息为"+e.getMessage();
}

@ExceptionHandler注解描述的方法为异常处理方法(注解中的异常类型为可处理的异常类型),假如Controller类中的逻辑方法中出现异常后没有处理异常,则会查找Controller类中有没有定义异常处理方法,假如定义了,且可以处理抛出的异常类型,则由异常处理方法处理异常。

控制层中的全局异常处理类及方法定义

当项目由多个控制层类中有多个共性异常的处理方法定义时,我们可以将这些方法提取到公共的父类对象中,但是这种方式是一种强耦合的实现,不利于代码的维护。我们还可以借助spring框架中web模块定义的全局异常处理规范进行实现,例如定义全局异常处理类,代码如下:

package com.cy.pj.common.web;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ArithmeticException.class)
    public String doHandleArithmeticException(ArithmeticException e){
        e.printStackTrace();
        return  "计算过程中出现了异常,异常信息为"+e.getMessage();
    }
}

其中,@RestControllerAdvice 注解描述的类为全局异常处理类,当控制层方法中的异常没有自己捕获,也没有定义其内部的异常处理方法,底层默认会查找全局异常处理类,调用对应的异常处理方法进行异常处理。

总结(Summary)

本小节主要是对springboot中的异常处理机制进行了简单分析和讲解。目的是掌握springboot工程下的异常处理方式,并基于业务的不同进行响应的异常处理。从而有效提高其用户体验,加强系统的容错能力。

查看原文

赞 14 收藏 8 评论 0

Jason 发布了文章 · 10月10日

10-SpringBoot 工程的健康监控实现

健康监控简述

Spring Boot 中actuator模块提供了健康检查,审计、指标收集,HTTP跟踪等功能,可以帮助我们更好的管理和跟踪springboot项目。

健康监控配置实现

在需要使用健康监控的项目或module中,添加如下依赖:

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

添加完依赖以后,reload项目或module。

健康监控启动分析

启动项目,在浏览器中输入如下地址:(SpringBoot默认打开的监控选项有限)

http://localhost/actuator

其访问结果,如图所示:

image.png

还可以在actuator列出的选中中进行点击,例如访问health

http://localhost/actuator/health

其呈现结果,如图所示(其中up状态表示正常):
image.png

假如希望查看更多actuator选项,可以在spring boot中配置文件

application.properties中添加如下语句:

management.endpoints.web.exposure.include=***

然后,重启服务器,基于访问http://localhost/actuator地址...

总结(Summary)

本小节主要是对springboot工程中的健康监控功能做了一个简易分析和实现,自己可以基于业务的需要,进行监控的配置和分析.

查看原文

赞 13 收藏 7 评论 0

Jason 发布了文章 · 10月10日

09-springboot工程中的热部署实现。

热部署简介

Spring Boot 开发者为Spring Boot项目中提供了一个热部署(spring-boot-devtools)模块,支持项目的热部署(修改了某些资源以后无需重启服务),以提高开发效率.其底层其实是借助了两个类加载器做了具体实现,一个类加载器加载不变class,一个类加载器加载可能变化类,以提供类的热部署性能.

热部署环境初始化

本次热部署的演示在IDEA开发中进行实现,其它工具可以自己通过搜索引擎进行学习。

IDEA 启动自动编译,如图所示:

image.png

IDEA工具中启动注册窗口(按ctrl+shift+alt+/),如图所示:
image.png

选择编译构建配置,如图所示:
image.png

热部署在项目中应用

在需要热部署的项目或module中添加如下依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-devtools</artifactId>
   <scope>runtime</scope>
</dependency>

依赖下载以后,可重启reload项目,然后当我们修改了src/main/java目录下的java文件或修改了src/main/resources目录下的配置文件时,默认都会重启你的web服务器,但是修改了测试类或html文件不会自动重启和部署。

总结(summary)

本小节对springboot工程中的热部署进行了分析和实现,可以idea工具中实践整个过程。

查看原文

赞 13 收藏 8 评论 0

Jason 发布了文章 · 10月10日

08-springboot 工程下lombok的应用。

背景分析

在实际的java项目中我们创建的所有pojo类几乎都要为属性添加set/get/toString等相关方法,所有的日志记录相关类可能都要创建日志等对象,这些样板代码既没有技术含量,又影响着代码的美观,同时重复的编码过程会在无形中加大我们的工作量。 此时Lombok应运而生。

lombok简介

概述

Lombok是一个第三的Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来告诉编译过程中的编译工具,在源代码编译成字节码的过程中,在字节码中添加一些量样板代码。

常用注解分析

  • @Setter 用于为描述的类生成setter方法,不包含final修饰属性。
  • @Getter 用于为描述的类生成getter方法。
  • @ToString 用于为描述的类添加toString方法。
  • @EqualsAndHashCode 用于为描述的类,生成hashCode和equals方法。
  • @NoArgsConstructor 用于为描述的类生成无参的构造方法。
  • @AllArgsConstructor 用于为描述的类生成包含类中所有字段的构造方法。
  • @Data用于为描述的类生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
  • @Slf4J 用于为描述的类添加一个日志属性对象。

lombok安装

idea中的安装配置

第一步:打开idea的设置窗口,找到plugins菜单,搜索lombok进行安装,如图所示:

image.png

第二步:启动注解处理,如图所示:

image.png

第三步:重启idea(可选,有的idea版本需要)。

sts中的安装配置

自己百度尝试。

lombok在maven项目中应用

第一步:添加lombok依赖。

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>annotationProcessor</scope>
</dependency>

第二步:在类上应用lombok注解。

@Data 
@NoArgsConstructor 
@AllArgsConstructor 
public class Goods {
    private Long id;
    private String name;
    private String remark;
    private Date createdTime;
}

第三步:编写单元测试类检测Lombok注解应用

@Slf4j
@SpringBootTest
public class GoodsTests{
     @Test
     void testGoods(){
        Goods g=new Goods();
        g.setId(100L);
        g.setName("Lombok");
        log.info("id的值为{}",g.getId());
        log.info("name的值为{}",g.getName());
     }
}

总结(Summary)

本小节主要对lombok做了一个分析,安装和配置,并结合实际项目讲解了lombok的应用场景及具体应用过程。

查看原文

赞 10 收藏 7 评论 0

Jason 发布了文章 · 10月8日

04-VUE中的常用指令操作。

背景分析

传统的html并不支持表达式、分支语句、循环语句等结构的定义,为了弥补其不足很多前端框架,模板引擎通过在html元素中添加自定义属性,然后底层再借助解析引擎对html自定义属性进行处理,以这样的方式来增强html的功能。

Vue中的常用指令

v-bind

在html中假如希望元素的属性值随程序内容的发生而变化,可以借助v-bind或:代替,其基本语法如:

<元素 v-bind:属性名="变量或js表达式">

也可以采用其简化形式,其基本语法如:

<元素 :属性名="变量或js表达式">

案例分析:

    <div id="app">
        <font :color="color">hello</font>
    </div>
    <script>
        new Vue({
            el:"#app",
            data:{
                color:"red"
            }
        });
    </script>

v-show

v-show为一个专门控制元素的显示隐藏的特殊指令,其基本语法为:

<元素 v-show="bool类型的变量或返回bool类型的js表达式">

当Vue对象扫描到v-show时,就会立刻执行""中的变量或js表达式,
如果变量或js表达式的值为true,则什么也不干,元素保持原样显示。如果变量或js表达式的值为false,则new Vue()自动为当前元素添加display:none。

示例关键代码如下:

  <style>
    .pop{
      width:300px; height:150px;
      background-color:lightGreen;
    }
    .pop>.close{
      float:right;
      padding:5px 10px;
      cursor:pointer;
    }
  </style>
 <div id="app">
    <button @click="show">show</button>
    <div v-show="display" class="pop">
      <span class="close" @click="hide">×</span>
    </div>
 </div>
  <script>
        //2. 创建new Vue对象
        new Vue({
          el:"#app",
          data:{
            display:false
          },
          methods:{
            show(){
              this.display=true;
            },
            hide(){
              this.display=false;
            }
          }
        })
    </script>

v-if 和 v-else

v-if 和 v-else 是在html实现分支控制,二选一的一种实现方式,在使用时,v-if和v-else对应的的两个元素必须紧挨着写!中间不能插入其他元素,其基本语法为:

<元素1 v-if="boolean类型的变量或js表达式">
<元素2 v-else>

示例关键代码如下:


<div id="app">
    <!--第一个div是已登录时显示的内容-->
    <div v-if="isLogin"><h3>Welcome, dingding | <a href="javascript:;" @click="logout">注销</a></h3></div>
    <!--第二个div是未登录时显示的内容-->
    <div v-else><a href="javascript:;" @click="login">登录</a> | <a href="javascript:;">注册</a></div>
  </div>
  
  <script>
    new Vue({
      el:"#app",
      data:{
        isLogin:false //开局用户默认是未登录的
      },
      methods:{
        login(){
          this.isLogin=true;
        },
        logout(){
          this.isLogin=false;
        }
      }
    })
  </script>

v-else-if

v-else-if专门和v-if搭配使用,控制多个元素多选一显示的特殊的指令。其基本语法为:

<元素1 v-if="条件1">

<元素2 v-else-if="条件2">

<元素3 v-else-if="条件3">

... ...
<元素n v-else>

注意,v-if和v-else-if和v-else之间必须连着写,不能插入其他元素。

示例关键代码如下:

<div id="app">
    <span v-if="score>=90">优秀</span>
    <span v-else-if="score>=80">良好</span>
    <span v-else-if="score>=70">及格</span>
    <span v-else>及格</span>
 </div>
  <script>
    var vm=new Vue({
      el:"#app",
      data:{
        score:95
      }
    })
    //在控制台中: vm.socre=85
  </script>
 

v-for

v-for是一个专门用于根据数组内容反复生成多个相同结构的元素的特殊指令。其语法为:

<要反复生成的元素 v-for="(当前元素值, 当前位置) of 数组">

示例关键代码如下:

<div id="app">
    <!--需求: 根据tasks数组中的任务列表反复生成多个li-->
    <ul>
      <li v-for="(t,i) of tasks" :key="i">
      {{i+1}} - {{t}}
      </li>
    </ul>
  </div>
 <script>
    var vm=new Vue({
      el:"#app",
      data:{
        //用一个数组保存待办事项列表
        tasks:["吃饭", "睡觉"]
      }
    })
  </script>

v-for还可以遍历对象属性,例如:

<div id="app">
    <!--需求: 遍历lilei对象,在页面显示李磊对象的详细信息-->
    <ul>
      <li v-for="(value,key) of lilei" :key="key">
        {{key}} : {{value}}
      </li>
    </ul>
  </div>
  <script>
    var vm=new Vue({
      el:"#app",
      data:{
        lilei:{
          name:"Li Lei",
          age:11
        }
      }
    })
  </script>

v-for 还可以进行计数,例如:

  <div id="app">
    <ul>
      <li v-for="i of pageCount" :key="i">{{i}}</li>
    </ul>
  </div>
  <script>
    new Vue({
      el:"#app",
      data:{
        pageCount:5 //共5页
      }
    })
  </script>

v-on

v-on 是一个专门绑定事件处理函数的指令,其基本语法为:

<元素 v-on:事件名="事件处理函数名()">

v-on 也可以使用@符号代替,其基本语法结构为:


 <元素 @事件名="事件处理函数名()
 

如果事件处理函数不需要传入实参值,则()也可省略,例如:

<元素 @事件名="事件处理函数名">

凡是在页面中定义的事件处理函数,都要在new Vue()中的methods成员内添加对应的函数实体。

示例关键代码如下:

<div id="app">
<!--点哪个div,哪个div就可以喊自己疼!-->
<div id="d1" @click="say('html')">d1</div>
<div id="d2" @click="say('css')">d2</div>
</div>
  
 <script>
    new Vue({
      el:"#app",
      methods:{
        say(name){
          console.log(`hello ${name}!`)
        }
      }
    })
  <script>

v-html

如果{{}}绑定的是一段HTML片段时,是不会交给浏览器解析的。而是原样显示HTML片段的内容——和DOM中的textContent是一样的,假如希望html可被解析可以使用v-html代替{{}}。其基本语法结构为:

 <元素 v-html="变量"> 这个位置显示变量内容 </元素>

示例关键代码如下:

 <div id="app">
    <h3>{{msg}}</h3>
    <h3 v-html="msg"></h3>
  </div>
  <script>
    new Vue({
      el:"#app",
      data:{
        msg:`来自&lt;&lt;<a href="javascript:;">新华社</a>&gt;&gt;的消息`
      }
    })
  </script>

v-text

v-text是可代替{{}}绑定元素内容的特殊指令,用于设置元素内部的文本内容,而且可以防止因网络延迟短暂显示{{}}的现象。其语法为:

<元素 v-text="变量或js表达式"> </元素>

示例关键代码如下:

  <div id="app">
            <!--js的天下         -->
    <h3 v-text=`用户名:${uname}`></h3>
            <!--js的天下         -->
    <h3 v-text=`积分:${score}`></h3>
  </div>
  <script>
    setTimeout(function(){
      new Vue({
        el:"#app",
        data:{
          uname:"dingding",
          score:1000
        }
      })
    },6000)
  </script>

v-model

v-model 是用于实现双向绑定的指令,既能将程序中的变化,自动更新到页面上(model->view),又能将页面上发生的变化,更新回程序中的变量中(view->model),其基本语法为:

<表单元素 v-model:value="变量">

示例关键代码如下:

 <div id="app">
    <!--单向绑定: (Model->View 不能View->Model)-->
    <!-- <input :value="keyword"> -->
    <!--双向绑定: (Model->View 又能View->Model)-->
    <input v-model:value="keyword">
    <button @click="doUpdate">更新</button>
  </div>
  <script>
    var vm=new Vue({
      el:"#app",
      data:{
        keyword:"ABC" //开局用户没有输入任何关键词
      },
      methods:{
        doUpdate(){
          console.log(`查找 ${this.keyword} 相关的内容...`)
        }
      }
    })
  <script>
单选操作的双向绑定:

单选操作的value因为是写死的固定的备选值!用户在不同radio之前切换选中状态时,其实改变的是radio的checked属性值。所以,想用双向绑定获得当前选中的radio的值,应该绑定checked属性:

<input type="radio" value="固定值" v-model:checked="变量">

示例代码如下:

<div id="app">
    性别:
    <label><input type="radio" name="sex" value="1" v-model:checked="sex">男</label>
    <label><input type="radio" name="sex" value="0" v-model:checked="sex">女</label>
    <h3>您选的性别是:{{sex==1?"男":"女"}}</h3>
 </div>
 <script>
    new Vue({
      el:"#app",
      data:{
        sex:1
      }
    })
 </script>
  
select元素的双向绑定

一个select下包含多个option元素。所有备选值value,都分布在每个option上。但是每个备选值value也是写死的。用户每次选择不同的option时,其实select元素会将选中的option的value值,修改到select的value属性上。其语法为:

<select v-model:value="变量">
 <option value="值1">xxx</option>

 ... ...

 ... ...
</select>

示例代码如下:

 <div id="app">
    <select v-model:value="src">
      <option value="beijing">北京</option>
      <option value="shanghai">上海</option>
      <option value="hangzhou">杭州</option>
    </select><br/>
    <img :data-original="src">
  </div>
  <script>
    new Vue({
      el:"#app",
      data:{
        src:"beijing"
      }
    })
  </script>

总结(Summary)

本小节,主要是对vue中常用的的一些指令元素进行了分析,在理解和应用的过程中可以参考本文档和官方文档再结合实践进行熟练应用。

查看原文

赞 16 收藏 7 评论 0

Jason 发布了文章 · 10月8日

03-VUE中的插值( Interpolation)语法

背景分析

在传统的html页面中我们可以定义变量吗?当然不可以,那我们假如希望通过变量的方式实现页面内容的数据操作也是不可以的。当然我们可以在服务端通过定义html标签库方式,然后以html作为模板,在服务端解析也可以实现,但这样必须通过服务端进行处理,才可以做到,能不能通过一种技术直接在客户端html页面中实现呢?

VUE中的插值语法

这种语法是为了在html中添加变量,借助变量方式与js程序中的变量值进行同步,进而简化代码编写。其基本语法为:

<HTML元素>{{变量或js表达式}}</HTML元素>

在{{}}内部可以写:变量、算术计算、 三目、 访问数组元素、 创建对象、调用函数等,总之只要有返回的合法的js变量和表达式都行。但是不可以写程序结构(分支和循环)以及没有返回值的js表达式。

例如:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script data-original="js/vue.js"></script>
</head>
<body>
  <div id="app">
    <h3>用户名:{{username}}</h3>
    <h3>性别:{{gender==1?"男":"女"}}</h3>
    <h3>小计:¥{{(price*count).toFixed(2)}}</h3>
    <h3>下单时间: {{new Date(orderTime).toLocaleString()}}</h3>
    <h3>今天星期{{week[new Date().getDay()]}}</h3>
  </div>
  <script>
    new Vue({
      el:"#app",
      data:{
        uname:"dingding",
        gender:1,
        price:12.5,
        count:5,
        orderTime:1600228004389,
        week:["日","一","二","三","四","五","六"]
      }
    })
  </script>
</body>
</html>

总结(Summary)

本小节对VUE中{{}}语法进行了分析,通过这种语法,为以通过变量方式操作或同步js程序中的数据,提供了便利。

查看原文

赞 6 收藏 4 评论 0

Jason 发布了文章 · 10月8日

02-VUE中的MVVM设计分析

MVVM 简介

背景分析

在早期的web前端开发中,我们通常将页面构成分成三部分:

  • html 用于定义网页结构、呈现网页内容。
  • css 为页面元素添加相关样式。
  • JS 为页面添加交互行为。

在这种开发结构中,因为HTML和CSS中不支持变量,不支持分支、循环等程序结构,任何一个地方的修改都需要依赖js来实现。导致js中存在大量重复的步骤和编码!

MVVM设计概述

MVVM是一种设计思想,对早期的web前端开发做了增强,将前端代码按其职责的不同分成了三部分:

  • 界面(View)部分:由html+css构成,并允许在html,css中直接使用变量,表达式,语句等。
  • 模型部分(Model):一般为一个JSON格式的对象,用于存储view中需要的数据和函数。
  • 视图模型(ViewModel):专门负责自动维护模型对象中的变量与界面中的变量同步。

因为有了ViewModel的存在,数据可以自动同步到界面上,几乎避免了大量重复的编码!

VUE中的MVVM实现

设计分析

MVVM在VUE前端框架中的设计,如图所示:

image.png

代码实践分析

基于VUE中MVVM分析,先对代码实现过程进行分析,如图所示:

image.png

总结(Summary)

本小节讲解了MVVM的设计,以及MVVM在VUE框架中的基本结构,总之MVVM让前端代码结构更加清晰,简化了大量重复代码的编写,提高了代码的可维护性。

查看原文

赞 9 收藏 6 评论 0

Jason 发布了文章 · 9月30日

06-SpringBoot工程下Spring MVC技术的应用?

Spring MVC 简介

背景分析

在大型软件系统设计时,业务一般会相对复杂,假如所有业务实现的代码都纠缠在一起,会出现逻辑不清晰、可读性差,维护困难,改动一处就牵一发而动全身等问题。为了更好解决这个问题就有了我们现在常说的分层架构设计。

MVC 是什么

MVC是一种软件架构设计思想,基于MVC架构将我们的应用软件进行分层设计和实现,例如可以分为视图层(View),控制层(Controller),模型层(Model),通过这样的分层设计让我们程序具备更好的灵活性和可可扩展性.因为这样可以将一个复杂应用程序进行简化,实现各司其职,各尽所能.比较适合一个大型应用的开发.

Spring MVC 概述

Spring MVC是MVC设计思想在Spring框架中的一种实现,基于这样的思想spring框架设计了一些相关对象,用于更好的基于MVC架构处理请求和响应,其简易架构如图所示:

image.png

其中:
1)DispatcherServlet是客户端所有请求处理的入口,负责请求转发。
2)RequestMapping负责存储请求url到后端handler对象之间的映射。
3)Handler 用于处理DispatcherServlet对象转发过来的请求数据。
4)ViewResolver负责处理所有Handler对象响应结果中的view。

Spring MVC 快速入门

准备工作

第一步:创建项目module,基本信息如图所示:

image.png

第二步:添加项目依赖(可以在module创建时,也可以创建后),代码如下:

Spring Web 依赖(提供了spring mvc支持并且会嵌入一个tomcat)

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

Thymeleaf 依赖(提供了以html作为页面模板进行解析和操作的相关对象)

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

第三步:启动项目检测控制台启动状态是否OK

statics 目录分析及应用

statics 目录为springboot工程创建时添加了web依赖以后自动创建的目录,此目录中可以存储html、css、js、html等相关资源,这些资源可以在启动服务器以后,直接在浏览器进行访问。例如:
第一步:在statics目录下创建一个index.html页面,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
   <h1>The First Html Page</h1>
</body>
</html>

第二步:启动服务器并直接进行访问测试,如图所示

image.png

templates 目录分析及应用

templates 目录为springboot工程创建时添加了thymeleaf依赖以后自动创建的目录,此目录中要存储一些html模板,这个模板页面不能直接通过浏览器url进行访问,需要基于后端控制器,在方法中定义页面响应,例如:

第一步:定义TemplateController及方法,代码如下:

package com.cy.pj.health.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TemplateController {
      @RequestMapping("doTemplateUI")
      public String doTemplateUI(){
         return "default";
      }
}

第二步:在templates目录中定义模板页面default.html,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>The Default Template page</h1>
</body>
</html>

其中,假如default.html要在放在templates子目录中,则还需要在配置文件中配置thymeleaf的前缀,例如:

spring.thymeleaf.prefix=classpath:/templates/module/

第三步:启动服务进行访问测试,如图所示:

image.png

SpringMVC 响应数据处理

ModelAndView 应用

我们有一业务,现在需要将响应数据封装到ModelAndView对象,然后响应到客户端,如何实现呢?

第一步:定义ModelViewController以及方法,代码如下:

package com.cy.pj.module.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ModelViewController {
    @RequestMapping("/doModelAndView")
    public String doModelAndView(Model model) {
        model.addAttribute("username", "jason");
        model.addAttribute("state", true);
        return "default";
    }
}

第二步:在default.html页面中添加呈现数据的代码,例如:


<div>
    <ul>
        <li>username:[[${username}]]</li>
        <li>state:[[${state}]]</li>
    </ul>
</div>

第三步:启动项目进行访问测试,并检测输出结果,例如:

image.png

JSON数据响应

我们有一业务不需要页面,只需要将响应数据转换为json,然后响应到客户端,如何实现呢?
第一步:定义ReponseResult对象用于封装响应数据,例如:

package com.cy.pj.module.pojo;
public class ResponseResult {
    private Integer code;
    private String message;
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

第二步:定义JsonObjctController以及方法,代码如下:

package com.cy.pj.module.controller;

@RestController
public class JsonObjectController {

    @RequestMapping("/doConvertResponseToJson")
    public ResponseResult doConvertResponseToJson(){
        ResponseResult rs=new ResponseResult();
        rs.setCode(200);
        rs.setMessage("OK");
        return rs;
    }
    
    @RequestMapping("/doConvertMapToJson")
    public Map<String,Object> doConvertMapToJson(){
        Map<String,Object> map=new HashMap<>();
        map.put("username","刘德华");
        map.put("state",true);
        return map;
    }
    
    @RequestMapping("/doPrintJSON")
    public void doPrint(HttpServletResponse response)throws Exception{
        Map<String,Object> map=new HashMap<>();
        map.put("username","刘德华");
        map.put("state",true);
        //将map中的数据转换为json格式字符串
        ObjectMapper om=new ObjectMapper();
        String jsonStr=om.writeValueAsString(map);
        System.out.println("jsonStr="+jsonStr);
        //将字符串响应到客户端
        //设置响应数据的编码
        response.setCharacterEncoding("utf-8");
        //告诉客户端,要向它响应的数据类型为text/html,编码为utf-8.请以这种编码进行数据呈现
        response.setContentType("text/html;charset=utf-8");
        PrintWriter pw=response.getWriter();
        pw.println(jsonStr);
    }
    
}

第三步:启动服务器分别进行访问测试,代码如下:

image.png

SpingMVC 请求参数数据处理

我们在执行业务的过程中通常会将一些请求参数传递到服务端,服务端如何获取参数并注入给我们的方法参数的呢?

准备工作

定义一个controller对象,用户处理客户端请求,例如:

package com.cy.pj.module.controller;
import com.cy.pj.module.pojo.RequestParameter;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
public class ParamObjectController {}

直接量方式

在ParamObjectController中添加方法,基于直接量方式接受客户端请求参数,例如:

@GetMapping("/doParam01")
public String doMethodParam(String name){
 return "request params "+name;
}

访问时,可以这样传参,例如:

http://localhost/doParam01?name=beijing

POJO对象方式

定义pojo对象,用于接受客户端请求参数,例如:

package com.cy.pj.module.pojo;
public class RequestParameter {
    private String name;
    //......
 public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
 public String toString() {
        return "RequestParam{" +
                "name='" + name + ''' +
                '}';
    }
}

定义Controller方法,方法中使用pojo对象接收方法参数,例如:

@RequestMapping("/doParam02")
public String doMethodParam(RequestParameter param){//pojo对象接收请求参数,pojo对象中需提供与参数名相匹配的set方法
 return "request params "+param.toString();
}

启动服务进行访问测试,可以这样传参,例如:

http://localhost/doParam02?name=beijing

Map对象方式

有时候我们不想使用pojo对象接收请求参数,我们可以使用map对象来接收,又该如何实现呢?

定义Controller方法,基于map对象接收请求参数,例如:

@GetMapping("/doParam03")
public String doMethodParam(@RequestParam Map<String,Object> param){
 return "request params "+param.toString();
}

其中,map接收请求参数,必须使用@RequestParam对参数进行描述.

启动服务进行访问测试,可以这样传参,例如:

http://localhost/doParam03?name=beijing

总结(Summary)

本章节对springboot工程下spring mvc技术的应用做了一个入门实现,并结合实际项目中的业务应用,讲解了MVC中请求数据的获取和响应数据处理的一个基本过程.

查看原文

赞 29 收藏 11 评论 1

Jason 发布了文章 · 9月29日

05-SpringBoot工程中的MyBatis框架的整合实现及原理分析

整合MyBatis的初步分析

概述

Mybatis是一个优秀的持久层框架,底层基于JDBC实现与数据库的交互。并在JDBC操作的基础上做了封装和优化,它借助灵活的SQL定制,参数及结果集的映射方式,更好的适应了当前互联网技术的发展。Mybatis框架的简单应用架构如图所示:
image.png
在当今的互联网应用中项目,mybatis框架通常会由spring框架进行资源整合,作为数据层技术实现数据交互操作。

准备工作

第一步:创建项目module,例如:

image.png

第二步:添加依赖

mysql 驱动依赖

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
</dependency>

spring jdbc 依赖

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

mybatis starter依赖

<dependency>
 <groupId>org.mybatis.spring.boot</groupId>
 <artifactId>mybatis-spring-boot-starter</artifactId>
 <version>2.1.1</version>
</dependency>

第二步:application.properties 配置文件中添加简易配置

连接池配置

spring.datasource.url=jdbc:mysql:///dbgoods?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

mybatis配置

mybatis.mapper-locations=classpath:/mapper/*/*.xml

环境测试代码实现

在src/test/java目录中添加测试类,对mybatis框架整合进行基本测试,代码如下:

package com.cy.pj.goods.dao;
import java.sql.Connection;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MyBatisTests {
       @Autowired
       private SqlSession sqlSession;
       @Test
       public void testGetConnection() {
           Connection conn=sqlSession.getConnection();
           System.out.println("connection="+conn);
       }
}

在SpringBoot脚手架工程中,Spring框架会基于MyBatis框架底层配置,创建SqlSessionFactory对象,然后再通过此工厂对象创建SqlSession,最后基于Springku框架为测试类注入SqlSession对象,接下来,我们可以通过SqlSession对象实现与数据库的会话了。

image.png

整合MyBatis业务代码实现及原理分析

业务描述

基于SpringBoot脚手架工程对MyBatis框架的整合,实现对商品库中商品数据的查询业务。

API架构设计

image.png

业务时序图分析

image.png

业务代码设计及实现

第一步:定义商品模块POJO对象类型(基于此对象存储商品数据),代码如下:

package com.cy.pj.goods.pojo;
import java.util.Date;
/**用于存储商品信息的pojo对象*/
public class Goods {
    private Long id;
    private String name;
    private String remark;
    private Date createdTime;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
    public Date getCreatedTime() {
        return createdTime;
    }
    public void setCreatedTime(Date createdTime) {
        this.createdTime = createdTime;
    }
}

第二步:定义商品模块持久层对象GoodsDao接口及方法映射,代码如下

GoodsDao接口及方法定义

package com.cy.pj.goods.dao;
import com.cy.pj.goods.pojo.Goods;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**商品模块持久层对象,基于此对象的实现类操作商品库中的数据
 * @Mapper MyBatis框架中定义,用于描述持久层接口,告诉mybatis
 * 这个接口的实现类由mybatis创建,并且交给spring框架管理.
 * */
@Mapper
public interface GoodsDao {
    List<Goods> findGoods();
}

GoodsDao接口映射文件及SQL映射定义

<?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.cy.pj.goods.dao.GoodsDao">
     <select id="findGoods" resultType="com.cy.pj.goods.pojo.Goods">
         select * from tb_goods
     </select>
</mapper>

测试代码的编写及运行

定义单元测试类,对GoodsDao方法进行单元测试,例如:

package com.cy.pj.goods.dao;
@SpringBootTest
public class GoodsDaoTests {
    @Autowired
 private GoodsDao goodsDao;
    @Test
 void testFindGoods(){
        List<Goods> list=goodsDao.findGoods();
        for(Goods g:list){
            System.out.println(g);
        }
    }
}

测试运行,底层API分析,如图所示:

image.png

测试运行过程中的BUG分析

  • idea中@autowired注解错误提示配置,如图:

image.png

  • BindingException,如图所示:

image.png

  • 单元测试类位置错误,如图所示:

image.png

  • 找不到对应的映射元素,如图所示:

image.png

业务层记录MyBatis底层SQL会话时长

业务描述

现在有一个业务,需要记录数据持久层api方法调用时执行时长,如何实现?
要求:
1)我们不能直接将日志记录写到单元测试类中。
2)我们不能修改数据持久层实现类。

API架构设计

基于日志记录业务进行API设计,如图所示:

image.png

业务时序图分析

商品查询并进行日志记录,其运行时序分析,如图所示:

image.png

业务代码设计及实现

第一步:定义GoodsService接口,代码如下:

package com.cy.pj.goods.service;
import com.cy.pj.goods.pojo.Goods;
import java.util.List;
public interface GoodsService {
      List<Goods> findGoods();
}

第二步:定义GoodsServiceImpl实现类并进行日志记录,代码如下:

package com.cy.pj.goods.service;
import com.cy.pj.goods.dao.GoodsDao;
import com.cy.pj.goods.pojo.Goods;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class GoodsServiceImpl implements GoodsService{
   private static final Logger log= 
   LoggerFactory.getLogger(GoodsServiceImpl.class);
    @Autowired
 private GoodsDao goodsDao;
    @Override
 public List<Goods> findGoods() {
        long t1=System.currentTimeMillis();
        List<Goods> list=goodsDao.findGoods();
        long t2=System.currentTimeMillis();
        log.info("findGoods()->t2-t1={}",t2-t1);//日志输出
 return list;
    }
}

日志API应用分析,如图所示:

image.png

测试代码的编写及运行

编写单元测试类GoodsServiceTests,对GoodsService对象方法进行单元测试,例如:

package com.cy.pj.goods.service;
import com.cy.pj.goods.pojo.Goods;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class GoodsServiceTests {
    @Autowired
 private GoodsService goodsService;
    @Test
 void testFindGoods(){
        List<Goods> list=goodsService.findGoods();
        for(Goods g:list){
            System.out.println(g);
        }
    }
}

测试运行过程中的BUG分析

  • NullPointerException,如图所示:

image.png

  • NoSuchBeanDefinitionException,如图所示:

image.png

总结(Summary)

本小节主要是对MyBatis框架做了一个基本介绍,并且基于springboot工程做了mybatis框架的整合实现,重点掌握其应用架构,代码结构,编写过程、运行过程以及在实现过程中的问题分析和对应的解决方案。

查看原文

赞 62 收藏 22 评论 2

Jason 发布了文章 · 9月27日

03-基于IDEA创建SpringBoot项目并进行入门分析

SpringBoot 项目创建

创建Module

基于IDEA创建项目Module,模块名为04-springboot-start,组id和包名为com.cy,如图所示:
image.png
填写module信息,如图所示:
image.png
选择项目module版本,暂时不需要自己手动添加任何依赖,如图所示:
image.png
填写Module名称,完成module创建,如图所示
image.png

项目结构分析

项目Module创建好以后,其代码结构分析,如图所示:

image.png

SpringBoot 项目启动分析

启动入口

SpringBoot 工程中由SpringBootApplication注解描述的类为启动入口类,例如:

package com.cy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {//Application.class
  public static void main(String[] args) {//Main Thread
    SpringApplication.run(Application.class, args);
  }
}

启动过程概要分析

SpringBoot工程启动时其简易初始化过程,如图所示:

image.png

在启动过程中底层做了哪些事情,大致描述如下:
1)基于配置加载类(通过ClassLoader将指定位置的类读到内存->底层通过线程调用IO从磁盘读取到内存)。
2)对类进行分析(创建字节码对象-Class类型,通过反射获取器配置信息)。
3)对于指定配置(例如由spring特定注解描述)的对象存储其配置信息(借助BeanDefinition对象存储)。
4)基于BeanDefinition对象中class的配置构建类的实例(Bean对象),并进行bean对象的管理(可能会存储到bean池)。

SpringBoot 快速入门分析

业务描述

在项目Module中定义一个类,类名为DefaultCache,然后将此类对象交给Spring创建并管理。最后通过单元测试对类的实例进行分析。

API设计分析

基于业务描述,进行API及关系设计,如图所示:

image.png

代码编写及运行

基于业务及API设计,进行代码编写,其过程如下:

第一步:定义DefaultCache类

package com.cy.pj.common.cache;
import org.springframework.stereotype.Component;
/**
 * @Component 注解描述的类,表示此类交给Spring框架管理。
 */
@Component
public class DefaultCache {
}

第二步:定义DefaultCacheTests单元测试类

package com.cy.pj.common.cache;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@SpringBootTest
public class DefaultCacheTests {
    /**
 * @Autowired 注解描述的属性由spring框架按照一定规则为其注入值(赋值)
 * 赋值过程是怎样的?
 *  1)依赖查找?(请问查找规则是什么?)
 *  2)依赖注入?(需要借助什么技术?)
 */ @Autowired
 private DefaultCache defaultCache;
    @Test
 void testDefaultCache(){
        System.out.println(defaultCache.toString());
        //FAQ? defaultCache变量引用的对象是由谁创建的,存储到了哪里?bean pool
 }
}

第三步:运行单元测试类进行应用分析
启动运行单元测试方法,检测其输出结果,基于结果分析:
1)SpringBoot项目中Bean对象的构建。
2)SpringBoot项目中Bean对象的获取。

运行过程中的BUG分析

  • Bean类型找不到,如图所示:

image.png

  • 空指针异常(NullPointerExcetpion-NPE),如图所示:

image.png

SpringBoot 项目中的对象特性分析

准备工作

第一步:创建项目Module,例如名字为05-springboot-features,如图所示:
image.png

第二步:添加业务类ObjectPool,代码如下:

package com.cy.pj.common.pool;
@Component
public class ObjectPool{//假设此对象为一个对象池
    public ObjectPool(){//假设运行项目启动类,此构造方法执行了,说明此类对象构建了。
      Systemd.out.println("ObjectPool()")
    }
}

思考:一般池对象有什么特点?
1)在JVM内存会开辟一块相对比较大的空间。
2)在这块空间中存储一些对象(思考基于什么存储结构进行存储-数组,链表,散列表)。
3)基于“享元模式”设计思想,实现内存中对象的可重用性。

第三步:定义单元测试,代码如下:

package com.cy.pj.pool;
import com.cy.pj.common.pool.ObjectPool;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ObjectPoolTests {

    @Autowired
    private ObjectPool objectPool01;
    @Autowired
    private ObjectPool objectPool02;
    @Test
    void testObjectPool01(){
       System.out.println(objectPool01==objectPool02);
    }
}

延迟加载

现在思考一个问题,对于ObjectPool这个类,假如项目启动以后,暂时不会用到这个池对象,是否有必要对其进行创建(默认是会创建的)?我们知道没必要,因为占用内存。那如何在启动时不创建此类对象呢?借助Spring框架提供的延迟加载特性进行实现。例如,我们可以在需要延迟加载的类上使用@Lazy注解进行描述,代码如下:

package com.cy.pj.common.pool;
@Lazy
@Component
public class ObjectPool{//假设此对象为一个对象池
    public ObjectPool(){//假设运行项目启动类,此构造方法执行了,说明此类对象构建了。
      Systemd.out.println("ObjectPool()")
    }
}

此时,我们再去运行运行启动类,检测ObjectPool对象是否创建了,假如没有创建,说明延迟加载生效了。此时,我们总结一下,什么对象适合使用延迟加载特性呢?大对象,稀少用(项目启动以后,暂时用不到)的对象。
注意:延迟加载并不是延迟对类进行加载,而是在启动时,暂时不创建类的实例。假如想看一下内存中的类是否被加载了,可以通过JVM参数进行检测,参数为-XX:+TraceClassLoading。

对象作用域分析

在实际的项目中内存中的对象有一些可能要反复应用很多次,有一些可能用完以后再也不用了或者说应用次数很少了。对于经常要重复使用的对象我可考虑存储到池中(例如交给spring框架进行管理),应用次数很少的对象那就没必要放到池中了,用完以后让它自己销毁就可以了。在Spring项目工程中为了对这样的对象进行设计和管理,提供了作用域特性的支持,具体应用:

package com.cy.pj.common.pool;
@Scope("singleton")
@Lazy
@Component
public class ObjectPool{//假设此对象为一个对象池
    public ObjectPool(){//假设运行项目启动类,此构造方法执行了,说明此类对象构建了。
      Systemd.out.println("ObjectPool()")
    }
}

其中,在上面的代码中,我们使用了@Scope注解对类进行描述,用于指定类的实例作用域。不写@Scope默认就是单例(singleton)作用域,这个作用域会配合延迟加载(@Lazy)特性使用,表示此类的实例在需要时可以创建一份并且将其存储到spring的容器中(Bean池),需要的时候从池中取,以实现对象的可重用。假如一些对象应用次数非常少,可以考虑不放入池中,进而使用@Scope("prototype")作用域对类进行描述,让此类的对象何时需要何时创建,用完以后,当此对象不可达了,则可以直接被GC系统销毁。

对象生命周期方法

程序中的每个对象都有生命周期,对象创建,初始化,应用,销毁的这个过程称之为对象的生命周期。在对象创建以后要初始化,应用完成以后要销毁时执行的一些方法,我们可以称之为生命周期方法。但不见得每个对象都会定义生命周期方法。在实际项目中往往一些池对象通常会定义这样的一些生命周期方法(例如连接池)。那这样的方法在spring工程中如何进行标识呢?通常要借助@PostConstruct和@PreDestroy注解对特定方法进行描述,例如:

package com.cy.pj.common.pool;
@Scope("singleton")
@Lazy
@Component
public class ObjectPool{//假设此对象为一个对象池
    public ObjectPool(){
      Systemd.out.println("ObjectPool()")
    }
    @PostConstruct
    public void init(){
       System.out.println("init()");
    }
    @PreDestroy
    public void destory(){
     System.out.println("destory()");
    }
}

其中:
1)@PostConstruct 注解描述的方法为生命周期初始化方法,在对象构建以后执行.
2)@PreDestroy 注解描述的方法为生命周期销毁方法,此方法所在的对象,假如存储到了spring容器,那这个对象在从spring容器移除之前会先执行这个生命周期销毁方法(prototype作用域对象不执行此方法).

SpringBoot 项目中的依赖注入过程分析

在SpringBoot工程中,假如类与类之间存在着一定的依赖关系,Spring是如何进行依赖注入的呢,现在我们就通过一个案例做一个分析。

准备工作

第一步:创建一个项目module,如图所示:
image.png
第二步:启动运行项目,检测是否能成功启动

案例设计及分析

为了更好理解spring框架的底层注入机制,现在进行案例API设计,如图所示:

image.png

在这个案例中单元测试类CacheTests中定义一个Cache接口类型的属性,然后由Spring框架完成对cache类型属性值的注入。

代码编写及测试分析

第一步:定义Cache接口,代码如下:

package com.cy.pj.common.cache;
public interface Cache {
 
}

第二步:定义Cache接口实现类SoftCache,代码如下:

package com.cy.pj.common.cache;
 
@Component
public class SoftCache implements Cache{

}

第三步:定义Cache接口实现类WeakCache,代码如下:

package com.cy.pj.common.cache;
 
@Component
public class WeakCache implements Cache{

}

第四步:定义CacheTests单元测试类,代码如下:

package com.cy.pj.common.cache;
import org.junit.jupiter.api.Test;

@SpringBootTest    
public class CacheTests {
    @Autowired
    @Qualifier("softCache")
    private Cache cache;
    
    @Test
    public void testCache() {
        System.out.println(cache);
    }
}

其中,@Autowired由spring框架定义,用于描述类中属性或相关方法(例如构造方法)。Spring框架在项目运行时假如发现由他管理的Bean对象中有使用@Autowired注解描述的属性或方法,可以按照指定规则为属性赋值(DI)。其基本规则是:首先要检测容器中是否有与属性或方法参数类型相匹配的对象,假如有并且只有一个则直接注入。其次,假如检测到有多个,还会按照@Autowired描述的属性或方法参数名查找是否有名字匹配的对象,有则直接注入,没有则抛出异常。最后,假如我们有明确要求,必须要注入类型为指定类型,名字为指定名字的对象还可以使用@Qualifier注解对其属性或参数进行描述(此注解必须配合@Autowired注解使用)。

第五步:运行CacheTests检测输出结果,基于结果理解其注入规则。

编写及测试过程中的BUG分析

  • 依赖注入异常,如图所示:

image.png

总结(Summary)

本小节为springboot技术入门章节,主要讲述了SpringBoot工程下,spring中bean对象的编写,特性以及依赖注入的规则,希望通过这一小节的讲解,同学们能够理解我们为什么要将对象交给spring管理,spring管理对象有什么优势,我们在springboot工程中应该如何配置这些对象。

查看原文

赞 108 收藏 35 评论 17

Jason 发布了文章 · 9月24日

02-IDE工具之IDEA中Module的创建及Git基本操作。

创建项目Module并运行

创建并运行java module

在IDEA打开的项目中创建Java Module,如图所示:

image.png

在创建Java Module的界面,选择Next,输入module名,如图所示:

image.png

Java Module创建好以后的结构,如图所示:

image.png

在项目模块01-javase中创建包,例如:

image.png

在指定包中创建类,如图所示:

image.png

运行Java类,如图所示:

image.png

创建并运行Maven Module

在项目中,创建maven module,如图所示:

image.png

接下来,输入module基本信息,如图所示:

image.png

打开pom.xml文件,添加Junit依赖,如图所示:

image.png

创建包、单元测试类和方法,进行单元测试,如图所示:

image.png

创建并运行Spring Initializr Module

在项目中,创建Spring Initializr Module,如图所示:

image.png

接下来输入Spring Initializr Module信息,如图所示:

image.png

选择springboot版本,依赖,然后进入一下,如图所示:

image.png

输入module信息,然后完成Module的创建,如图所示:

image.png

image.png

Spring Initializr Module创建好以后,其结构如图所示:
image.png

Module创建好以后,可以打开pom.xml文件,修改其springboot版本,如图所示:
image.png

SpringBoot 版本修改好以后(可选),向pom.xml右键,选择Add as Maven Project 选项,进行maven项目构建(此时才会springboot 依赖下载和编译),例如:

image.png

Module 构建完成,打开启动类运行module,如图所示:

image.png

Module成功启动和运行结果,如图所示:

image.png

基于Git的项目操作

检查Git配置。

找到Git配置选项,进行Git测试,如图所示:

image.png

项目Push操作分析及实现

将项目、Module推送的Gitee远程代码托管平台,如图所示:

image.png

指定Gitee仓库的仓库名(库不存在,推送时自动创建),如图所示:

image.png

登陆Gitee,检查远程仓库内容,如图所示:

image.png

项目Add,Commit操作分析及实现

将项目、Module更新添加到暂存区,提交(Commit)本地库,例如:

image.png

也可以,基于工具栏的按钮进行相关操作,如图所示:

image.png

假如,团队中其他成员需要这个项目、Module时可以直接进行克隆。

从Gitee地址克隆(Clone)项目

打开IDEA中可克隆(clone)选项,如图所示:
image.png

指定克隆地址和克隆目录,如图所示:
image.png

克隆完成以后,选择打开项目的窗口,例如:

image.png

总结(Summary)

本章节中讲解了IDEA工具下Java Module,Maven Module,Spring Boot Module 的创建,运行以及将项目提交到Gitee平台或从Gitee平台克隆项目的基本过程。

查看原文

赞 61 收藏 28 评论 2

Jason 发布了文章 · 9月24日

01-IDE工具之IDEA的简介、下载与安装、初步配置。

IDEA简介

概述

IDEA全称是IntelliJ,是JetBrains公司推出一个集成开发工具,是Java开发工具中的翘楚,基于这个开发工具可以快速开发我们的Java相关项目。相对于其它开发工具,IDEA提供了更加强大的提示功能,全面的快捷键操作,模板代码以及快速的资源整合。

下载与安装

准备工作

  • 硬件(Hardware)要求

    1)内存8G或以上
    2)硬盘磁盘空闲空间大于5GB,最好有一块固态硬盘。

  • 软件(Software)要求

    1)Microsoft Windows 8 or later
    2)macOS 10.13 or later
    3)Any Linux distribution
    4)Set Up and Config JDK1.8

IDEA工具下载

IDEA官网下载地址如下:

https://www.jetbrains.com/idea/

基于操作系统的不同,可以选择不同平台下的IDEA,例如:
image.png

说明:至于社区版(功能受限的免费版)/还是商业版(可试用)自行决定。

IDEA工具安装

IDEA 下载以后需要进行安装进行使用。假如只是下载的解压版本,则可以将其拷贝到非中文目录,然后直接解压进行应用。假如下载的可执行程序(例如windows平台下的.exe文件)则直接启动可执行程序,然后按照提示,一步一步安装即可。

IDEA官方帮助文档文档参考如下:

https://www.jetbrains.com/help/idea/discover-intellij-idea.html

IDEA工具启动

找到解压或安装好的idea根目录,然后进入bin目录找到idea.bat或者idea64.exe文件,然后直接双击进行启动,第一个启动欢迎界面如图所示:

image.png

在第一个界面呈现过程中,其实是IDEA底层在进行启动初始化,假如是第一次启动,稍等片刻会进入第二个界面,例如:

image.png

在第二个界面中,我们先选择创建新项目(New Project),然后进入创建项目的界面,这里我们先选择创建空项目,如图所示:

image.png

选择好Empty Project以后,点击next进入项目的设计,如图所示:

image.png

在上面图中,点击Finish按钮完成项目的创建,进入IDEA的项目工作窗口,如图所示:

image.png

IDEA 基本配置

Appearance(显示)主题配置

image.png

KeyMap(快捷键)配置

image.png

Editor(编辑区)配置

General 配置

滚轮滑动,改变字体大小配置,如图所示:
image.png
包导入配置,如图所示:
image.png
行号与方法分隔符配置,如图所示:
image.png
代码提示配置,如图所示:
image.png
代码补全配置,如图所示:
image.png

Font 配置

代码编辑区字体配置,如图所示:
image.png
控制台字体配置,如图所示:
image.png

Color 配置

代码中的注释颜色配置,如图所示:
image.png

File 编码配置

image.png

Build 执行配置

Compiler(编译配置)

自动编译配置(可选),选上了可能会比较慢,如图所示:
image.png

Build Tools 中的Maven配置

Maven 初始化配置(假如已有配置好的,可以使用已有的配置)

找到你磁盘中的maven根目录下的conf目录,并打开conf目录下的settings.xml文件,然后进行如下配置:

  • 配置maven本地库(从maven远程服务器下载的资源存储到的位置)
<localRepository>${user.home}/.m5/repository</localRepository>
  • 配置maven私服(配置到mirrors标签内部)。
   <mirror>
    <id>aliyun</id>
    <name>aliyun for maven</name>
    <mirrorOf>*</mirrorOf>
    <url>https://maven.aliyun.com/repository/public</url>
   </mirror>
  • 配置maven中的profile(配置到profiles标签内部),设置JDK编译和运行版本。
 <profile>
   <id>jdk-1.8</id>
   <activation>
    <activeByDefault>true</activeByDefault>
      <jdk>1.8</jdk>
   </activation>
   <properties>
       <maven.compiler.source>1.8</maven.compiler.source>
       <maven.compiler.target>1.8</maven.compiler.target>
       <maven.compiler.compilerVersion>1.8
       </maven.compiler.compilerVersion>
   </properties>
 </profile>
IDEA 中集成Maven配置

找到Build Tools/Maven选项,然后在IDEA中集成本地maven配置。如图所示:

image.png

Version Control 配置

安装gitee插件

打开Setting中的Plugins选项,然后进行gitee插件搜索和安装,如图所示:
image.png

Gitee 安装成功以后,检查是否在Version Control中有Gitee选项,如图所示:
image.png

点击Version Control的Gitee选项,进入Gitee配置,如图所示:
image.png

在Gitee配置界面,选则添加账户(Add Account),进入账户配置界面,如图所示:
image.png

在Gitee账户配置界面,进行连接Gitee平台的账户配置(要事先注册好Gitee平台账户),然后点击Log in进行登陆,登陆成功以后会呈现如下界面,如图所示:

image.png

总结(Summary)

本小节主要是对IDEA开发工具进行了一个简单介绍,然后基于我们后续要做的应用,做了一个初步配置,基于这个配置我们后续可以更好的展开项目的创建、测试等。

查看原文

赞 85 收藏 45 评论 7

Jason 发布了文章 · 9月22日

01-Git应用入门

背景分析

软件项目开发过程中,团队间共享的代码,文档等可能被别人或自己不小心覆盖或遗失、也不知道是谁,因为什么原因改了这段代码、也没办法很好的复原前几天的修改,于是版本控制系统(VCS-Version Control System)诞生。

有了版本控制系统,我们可以浏览所有开发的历史纪录,掌握团队的开发进度,而且作任何修改都不再害怕,因为你可以轻易的复原回之前正常的版本。我们也可以透过分支和标签的功能来进行软件发行的不同版本,例如稳定版本、维护版本和开发中版本。近几年版本控制系统的应用趋势,如图所示:

image.png

版本控制系统(VCS,Version Control System)可以划分为集中式和分布式两大类。集中式顾名思义,是用单一的服务器来集中管理保存项目的所有文件。项目团队的成员通过客户端连接到这台服务器,下载或提交文件。如图所示:

image.png

集中式客户端一旦无法连接服务器,那么版本控制功能将无法使用(例如比较历史版本差异;查看某个历史版本内容等)。集中式的VCS杰出代表是SVN.

分布式的特点是每个客户端除了可以连接到一个集中的服务器外,客户端本身可以是一个完整的版本控制仓库,项目团队成员可以在自己的电脑上对文件进行版本管理。如图所示:

image.png

与集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。分布式的VCS杰出代表是git。

Git简介(Introduction)

Git是一个开源的分布式版本控制系统,作者是传奇人物Linus,著名的开源操作系统Linux作者。liuns花了两周时间自己用C写出了一个Git。现在Linux系统的源码都是由Git进行管理。Git现已是VCS领域的江湖霸主。

Git安装(Setting Up)

下载与安装

Windows和Mac系统, 可以直接从 http://git-scm.com/downloads 网址下载并运行安装程序。对于Windows用户可使用 "Git Bash" 命令行工具(Git安装时自带)使用Git,Mac系统可以直接实用终端窗口"Terminal"。

全局用户信息配置

配置用户和密码

$ git config --global user.name "your-name"
$ git config --global user.email "your-email@youremail.com"

检查配置信息

$ git config --list
user.email=xxxxxx@xxxxxx.com
user.name=xxxxxx

Git基础(Basic)应用实现

Git 命令简介

Git提供了一组简单、独特、独立的命令,这些命令的执行需要首先启动windows或mac系统的Git终端(window 下可以可使用 "Git Bash"),其语法结构为:

$ git <command> <arguments>

Git 常用命令有:

init, clone, config: 用于启动Git进行项目管理。
add, mv, rm: 用于暂时记录或存储文件的变更.
commit, rebase, reset, tag:
status, log, diff, grep, show: show status
checkout, branch, merge, push, fetch, pull

Git 帮助手册:(可快速获取命令的使用帮助)

$ git help <command>
// or
$ git <command> --help

Git 操作手册官方文档 http://git-scm.com/docs.

Git 快速入门

这里有两种方式启动Git管理项目:

  • 创建或启动一个新项目,然后进行git操作。
  • 从一个Git主机上克隆一个已经存在的项目。

这里我们先从新项目开始,进行入门学习:

首先创建一个git工作目录,命名为githello,然后在目录中创建一个一个Hello.java,其代码为:

public class Hello {
   public static void main(String[] args) {
      System.out.println("Hello, world from GIT!");
   }
}

建议,在Hello.java所在目录同样再创建一个README.md文件,通过此文件对你的项目进行描述,例如在文件中添加如下内容:

This is the README file for the Hello-world project.
  • 初始化git仓库

基于Git实现项目管理时,首先在项目所在的根目录(例如githello)执行"git init",过程如下:

git init 

// Change directory to the project directory
//$ cd /path-to/hello-git
 
// Initialize Git repo for this project

$ git init
Initialized empty Git repository in /path-to/hello-git/.git/

$ ls -al
drwxr-xr-x    1 xxxxx    xxxxx      4096 Sep 14 14:58 .git
-rw-r--r--    1 xxxxx    xxxxx      426 Sep 14 14:40 Hello.java
-rw-r--r--    1 xxxxx    xxxxx       66 Sep 14 14:33 README.md

其中,git init 指令执行结束,git本地库的根目录中会出啊关键一个".git"目录(默认此目录为隐藏状态)。

Git 存储模型,如图所示:

image.png

当我们执行了git init 指令以后,本地库其实是空的的。您需要显式地将文件存入仓库。

  • 暂存和更新文件的变更。
// Add README.md file
$ git add README.md
 
$ git status
On branch master
Initial commit
 
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   README.md
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        Hello.java
 
// You can use wildcard * in the filename
// Add all Java source files into Git repo
$ git add *.java
 
// You can also include multiple files in the "git add"
// E.g.,
// git add Hello.java README.md
 
$ git status
On branch master
Initial commit
 
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   Hello.java
        new file:   README.md
1)命令“git add<file>..”接受一个或多个可能带有通配符模式的文件名或路径名。您还可以使用“git add.”添加当前目录(以及所有子目录)中的所有文件。
2)当一个新的文件被执行了“add”操作以后,它将存储到git的暂存区(还并没有commit到本地库)。

Git 使用两道工序提交文件的变更:
1)"git add <file>" 将文件存储到暂存区。
2)"git commit" 将文件的所有变更由暂存取提交到本地库。

  • 提交文件变更(git commit)

    假如我们要将git暂存区文件的变更信息提交到本体库,需要实用"git commit"指令,例如:

$ git commit -m "First commit"
[master (root-commit) 6e49a29] first commit
 2 files changed, 1 insertion(+)
 create mode 100644 Hello.java
 create mode 100644 README.md

接下来还可以查看Git状态:

$ git status

显示Git提交日志信息:

Git每次都会记录提交的元数据,其中包括日志消息、时间戳、作者的用户名和电子邮件等)。我们可以使用"git log"指令显示提交的数据。也可以使用"git log --stat"查看文件的统计(statistics)信息。

$ git log
git log --stat
  • .gitignore 文件

在Git中,有一种特殊的文件,其文件全名就是 .gitignore,,主要功能是基于规则屏蔽某些文件,使得这些文件不被追踪(tracked),自然push后也不会上传到github等平台。这个时候我们只需要在需要执行push操作的目录下创建.gitignore文件,然后定义其规则即可,例如:

# .gitignore
 
# Java class files
*.class

# Executable files
*.exe

# temp sub-directory (ended with a directory separator)
temp/

现在,执行“git status”命令来检查未跟踪(untracked)的文件。

$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
      .gitignore
nothing added to commit but untracked files present (use "git add" to track)

通常(Typically),我们也会跟踪或提交.gitignore文件,例如:

$ git add .gitignore
 
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
        new file:   .gitignore
 
$ git commit -m "Added .gitignore"
[master 711ef4f] Added .gitignore
 1 file changed, 14 insertions(+)
 create mode 100644 .gitignore
  
$ git status
On branch master
nothing to commit, working directory clean

Git远程仓库设置

我们使用Git进行项目管理时,通常会将项目提交到远程仓库,其具体步骤如下:
第一步:创建远程仓库(例如Gitee)-自己创建。
第二步:设置远程仓库名和url映射。
通过 via "git remote add <remote-name> <remote-url>"指令完成,设置名字为 "origin"的远程仓库绑定。
例如:

$ git remote add origin https://gitee.com/JasonCN2008/gittest.git

以列表方式呈现远程仓库名:

$ git remote -v

第三步:推送(Push)和提交(commit)本库内容到远程仓库。

将本地库内容推送到远程库(执行过程需要输入远程仓库账号和密码),其语法为git push -u <remote-name> <local-branch-name>,例如:

$ git push origin master
实践过程演示:

修改Hello.java文件内容,例如:

public class Hello {
   public static void main(String[] args) {
      System.out.println("Hello, world from GIT!");
      System.out.println("Push and Commit !");
   }
}

执行Git操作

$ git status
// Stage file changes

$ git add *.java

$ git status

// Commit all staged file changes
$ git commit -m "Third commit"
[master 744307e] Third commit
 1 file changed, 1 insertion(+)
 
// Push the commits on local master branch to remote
$ git push origin master

从远程仓库克隆项目

执行 "git clone <remote-url>" 初始化一个本地库,并将remote-url远程库中资源拷贝到本地库工作目录。例如,首先切换到git工作目录,然后执行:

$ git clone https://gitee.com/JasonCN2008/gittest.git githello-clone

Clone 结束以后,切换到githello-clone目录,执行ls -a指令查看目录中资源。

总结(Summary)

本小节主要是对Git的基本指令操作进行了分析和实践,我们现在再将这些这令总结一下:

// Edit (Create, Modified, Rename, Delete) files。 
// Stage file changes, which produces "Staged" file changes
$ git add <file>                          // for new and modified files
$ git rm <file>                           // for deleted files
$ git mv <old-file-name> <new-file-name>  // for renamed file

// Commit (ALL staged file changes)
$ git commit -m "message"

// Push
$ git push <remote-name> <local-branch-name>
查看原文

赞 15 收藏 10 评论 0

Jason 发布了文章 · 9月5日

你了解JavaScript中构造函数的构造函数吗?

背景分析

当我们需要,多个结构相同(例如属性名相同,属性值不同)的对象时如何定义)?例如:

var p1={x:10,y:20}
var p2={x:30,y:40}
var p3={x:50,y:60}

在如上代码中,假如属性比较多,构建对象时需要反复对属性进行编写,代码的重复量就比较大,同样会加大我们的工作量,那如何对其进行优化呢?

JS中构造函数定义

JS中的构造函数允许我们对JS对象进行抽象,提取对象结构进行封装,
然后构建对象时基于构造函数定义的结构进行实例化.

传统方式构造函数定义

image.png

class结构方式的构造函数定义

在ES6标准中,又推出了定义JS构造函数的一种新的玩法,例如:
image.png

JS中构造函数应用

无论是传统方式还是新的es6方式,对象的构建和使用方式是不变的。
image.png

image.png

总结(Summary)

本小节中重点讲解了JS中构造函数存在的意义,应用场景,构造函数的定义,以及基于构造函数构建对象的方式。

查看原文

赞 29 收藏 10 评论 0

Jason 赞了文章 · 9月5日

递归2寻路之dfs

练习:

1给定一个迷宫矩阵,入口,出口,输出走出迷### 宫的所有路径

代码:
public class P1 {
static int m=4,n=6;
int[][] map= {
        {1,1,1,1,1,1},
        {0,1,0,1,0,1},
        {1,1,0,1,0,1},
        {1,1,1,1,1,1}        
};
int dir[][]= {{1,0},{0,1},{-1,0},{0,-1}};
int arr[]=new int[m*n*m];
int length=0;
void f1(int x,int y) {
    if(x==m-1&&y==n-1) {
        for(int i=0;i<length;i++)
            System.out.print(arr[i]+","+(i%2==0?"":" "));
        System.out.println();
    return;
    }
    for(int i=0;i<4;i++) {
        int x1=x+dir[i][0];
        int y1=y+dir[i][1];
        if(x1>=0&&x1<m&&y1>=0&&y1<n&&map[x1][y1]==1) 
        {    
            map[x1][y1]=2;
            arr[length++]=x1;
            arr[length++]=y1;
            f1(x1, y1);
            map[x1][y1]=1;
            length-=2;
        }    
    }            
}
public static void main(String[] a)    {
    new P1().f1(0, 0);
}
}

输出:
0,1, 1,1, 2,1, 3,1, 3,2, 3,3, 3,4, 3,5,
0,1, 1,1, 2,1, 3,1, 3,2, 3,3, 2,3, 1,3, 0,3, 0,4, 0,5, 1,5, 2,5, 3,5,
0,1, 1,1, 2,1, 2,0, 3,0, 3,1, 3,2, 3,3, 3,4, 3,5,
0,1, 1,1, 2,1, 2,0, 3,0, 3,1, 3,2, 3,3, 2,3, 1,3, 0,3, 0,4, 0,5, 1,5, 2,5, 3,5,
0,1, 0,2, 0,3, 1,3, 2,3, 3,3, 3,4, 3,5,
0,1, 0,2, 0,3, 0,4, 0,5, 1,5, 2,5, 3,5,

思路:
从起点向四个方向遍历,如果可以通行则该位置通行,存在数组arr中,并进行标注,如果走到终点输出arr中的值

下面开始正题:

蓝桥杯 方格分割

标题:方格分割

6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。

如图:就是可行的分割法。
image
image
image
试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法。

请提交该整数,不要填写任何多余的内容或说明文字。

 解题思路:题目要求沿着格子的边线剪成两个部分,仔细观察,剪开的边线是关于中心点(3,3)对称的,于是我们从点(3,3)开始搜索,

public class fgfg {
public static int N=6;
public static int [][]map=new int[N+1][N+1];
public static int count=0;
public static int [][]dir= {{0,1},{1,0},{0,-1},{-1,0}};
public static void f1(int x,int y) {
    if(x==0||y==0||x==N||y==N) {
        count++;
        return;
    }else {
        for(int i=0;i<4;i++) {
            int x1=x+dir[i][0];
            int y1=y+dir[i][1];
            if(map[x1][y1]==1)
                continue;
            map[x1][y1]=map[N-x1][N-y1]=1;
            f1(x1,y1);
            map[x1][y1]=map[N-x1][N-y1]=0;    
        }    
    }    
}
public static void main(String[] args) {
    map[N/2][N/2]=1;     
    f1(3,3);    
    System.out.println(count/4);
}
}

输出:
509

小练习:

输入
第一行输入一个整数N,表示共有N组测试数据
每一组数据都是先输入该地图的行数m(0<m<100)与列数n(0<n<100),
然后,输入接下来的m行每行输入n个数,表示此处有水还是没水
(1表示此处是水池,0表示此处是地面)
输出
输出该地图中水池的个数。
每个水池的旁边(上下左右四个位置)如果还是水池的话的话,它们可以看做是同一个水池。

---------------------------------------------------------Zzh

查看原文

赞 7 收藏 3 评论 1