Jason

Jason 查看完整档案

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

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

个人动态

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中构造函数存在的意义,应用场景,构造函数的定义,以及基于构造函数构建对象的方式。

查看原文

赞 30 收藏 11 评论 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

Jason 赞了文章 · 9月5日

花里胡哨1之网上炫酷的字符动画怎么写---------------java

思路:把视频的每一帧保存在一个图片,并把图片转成字符串输出,这里我用的是opencv库

public class day2 extends JFrame {
static final String ascii = "#u:, ";
static final int textWidth = 190;
static final int textHeight = 50;
Container container;
JTextArea jTextArea;
static {
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  } 
public day2() {
    setBounds(100, 100, 900, 600);
    setTitle("动画");
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    container = getContentPane();
    jTextArea = new JTextArea();
    jTextArea.setFont(new Font("楷体", Font.PLAIN, 8));
    container.add(jTextArea);
    setResizable(false);
    setVisible(true);
}
public void put(String string) {
    VideoCapture capture = new VideoCapture(string.toString());    
    if (!capture.isOpened()) 
        return;
    HighGui.namedWindow("show");
    Mat img1 = new Mat();
    while (capture.read(img1)) {
    HighGui.imshow("show", img1);
    HighGui.waitKey(30);
        jTextArea.setText(imgTest(img1).toString());
    }
}
public StringBuilder imgTest(Mat img1) {
    Mat img2=new Mat();
    int index, gray;
    Imgproc.cvtColor(img1, img2, Imgproc.COLOR_RGB2GRAY);
    int x = img2.rows() / textHeight;
    int y = img2.cols() / textWidth;
    StringBuilder src = new StringBuilder();
    for (int i = 0; i < img2.rows(); i += x) {
        for (int j = 0; j < img2.cols(); j += y) {
            gray = (int) (img2.get(i, j)[0]);
            index = Math.round(gray * (ascii.length() + 1) / 255);
            src.append(index >= ascii.length() ? "." : String.valueOf(ascii.charAt(index)));
        }
        src.append("\n");
    }
    return src;
}
public static void main(String[] a) {
new day2().put("F:/tupian/a5.mp4");
}}

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

查看原文

赞 4 收藏 2 评论 3

Jason 发布了文章 · 9月5日

13-SpringBoot工程下活动(Activity)模块设计及实现?

业务描述

基于Spring,MyBatis,SpringBoot,Thymeleaf,Ajax技术实现活动模块的查询,添加等操作。

项目环境初始化

准备工作

1. MySQL(5.7)
2. JDK (1.8)
3. Maven (3.6.3)
4. STS(4.7.1)

数据库初始化

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

mysql –uroot –proot

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

set names utf8;

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

source d:/activity.sql

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

drop database if exists dbactivity;
create database dbactivity default character set utf8;
use dbactivity;
create table tb_activity(
     id bigint primary key auto_increment,
     title varchar(100) not null,
     category varchar(100) not null,
     startTime datetime not null,
     endTime datetime not null,
     remark text,
     state tinyint,
     createdTime datetime not null,
     createdUser varchar(100)
)engine=InnoDB;
insert into tb_activity values (null,'ABS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'VALIDATE','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'VALIDATE','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'ABS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'ACCESS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');

创建项目并添加依赖

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

image.png

第二步:创建项目时指定项目核心依赖。
image.png

第三步:项目创建以后分析其结构。

image.png

项目配置文件内容初始化

#server
server.port=80
#server.servlet.context-path=/
#spring datasource
spring.datasource.url=jdbc:mysql:///dbactivity?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=debug

#spring thymeleaf
spring.thymeleaf.prefix=classpath:/templates/modules/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false

项目API架构设计

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

活动模块查询业务实现

业务描述

从数据库查询所有活动(Activity)信息,然后呈现在页面上(基于JS方式进行呈现),如图所示:

image.png

业务时序分析

在浏览器地址栏输入查询活动信息的url获取活动信息,其时序过程如图所示:

image.png

POJO类设计及实现

创建Activity类,基于此类对象封装从数据库获取的活动信息,代码如下:

package com.cy.pj.activity.pojo;
import java.util.Date;
import lombok.Data;
@Data
public class Activity {
    private Integer id;
    private String title;
    private String category;
    private Date startTime;
    private Date endTime;
    private String remark;
    private Integer state;
    private Date createdTime;
    private String createdUser;
}
其中,@Data注解为Lombok注解,需要在IDE环境中已经安装lombok插件,项目中添加lombok依赖,假如没有也可以自己添加set/get相关方法

Dao接口方法及映射定义

定义活动模块数据层接口及查询方法,代码如下:

package com.cy.pj.activity.dao;
import com.cy.pj.activity.pojo.Activity;

@Mapper
public interface ActivityDao {
    
     @Select("select * from tb_activity order by createdTime desc")
     List<Activity> findActivitys();
}

Service接口方法定义及实现

定义service接口以及获取活动信息的方法,代码如下:

package com.cy.pj.activity.service;
import java.util.List;
import com.cy.pj.activity.pojo.Activity;
//引入包中的类:ctrl+shift+o

public interface ActivityService {
    List<Activity> findActivitys();
}

定义service接口实现类,并重写接口方法,代码如下:

package com.cy.pj.activity.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.cy.pj.activity.dao.ActivityDao;
import com.cy.pj.activity.pojo.Activity;
import com.cy.pj.activity.service.ActivityService;

@Service
public class ActivityServiceImpl implements ActivityService {
    @Autowired
    private ActivityDao activityDao;
    @Override
    public List<Activity> findActivitys() {
         return  activityDao.findActivitys();
    }
}

Controller方法定义及实现

package com.cy.pj.activity.controller;

import java.util.List;

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 com.cy.pj.activity.pojo.Activity;
import com.cy.pj.activity.service.ActivityService;

@Controller
public class ActivityController {
     @Autowired
     private ActivityService activityService;
     
     @RequestMapping("/activity/doActivityUI")
     public String doActivityUI(){
          return "activity";
     }
     
     /**查询所有活动信息*/
     @RequestMapping("/activity/doFindActivitys")
     @ResponseBody
     public List<Activity> doFindActivitys() {
         List<Activity>  list=activityService.findActivitys();
         return list;
     }
}

Activity 列表页面设计及实现

本次页面样式基于bootstrap(一个前端框架,官网为bootcss.com)进行实现,首先在项目工程中添加静态资源,如图所示:

image.png
设计activity.html页面,引入bootstrap,jquery等相关资源,如图所示:

image.png

在activity.html页面中添加呈现Activity数据的html元素:

<table class="table">
    <thead>
        <tr>
            <th>title</th>
            <th>Category</th>
            <th>StartTime</th>
            <th>EndTime</th>
            <th>State</th>
            <th>Operation</th>
        </tr>
    </thead>
    <tbody id="tbodyId">
        <tr><td colspan="6">数据正在加载中.......</td></tr>
    </tbody>
</table>

基于Ajax异步加载服务端活动数据并进行局部刷新

<script type="text/javascript">
      function doFindActivitys(){
          var url="/activity/doFindActivitys"
          //启动ajax技术,基于GET请求方式获取服务端json数据
          //getJSON函数默认会将服务端返回的json串转换为js对象
          $.getJSON(url,function(result){
              var tBody=$("#tbodyId");
              tBody.empty();//清空原有body内容
              for(var i=0;i<result.length;i++){//循环一次,迭代一行
                  //构建当前行对象
                  var tr=`<tr>
                          <td>${result[i].title}</td>
                          <td>${result[i].category}</td>
                          <td>${result[i].startTime}</td>
                          <td>${result[i].endTime}</td>
                          <td>${result[i].state==1?'有效':'无效'}</td>
                          <td><button type='button' class='btn btn-danger btn-sm'>delete</button></td>
                         </tr>`
                  //将每一行的内容都追加tbody中
                  tBody.append(tr);
              }
          });
      };
      doFindActivitys();
    </script>

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

启动项目,在浏览器中输入activity页面的访问地址,呈现活动信息,如图所示:

image.png

客户端断点调试分析,如图所示:

image.png

运行时断点设置,如图所示:

image.png

进入断点方法内部,如图所示:

image.png

检查响应数据在客户端的呈现,如图所示:

image.png

项目启动及运行过程中的BUG分析

  • 数据库链接的url配置错误,如图所示:

image.png

  • 空指针异常,如图所示:

image.png

  • JS 引入错误,如图所示:

image.png

image.png

image.png

  • JS 语法错误,如图所示:

image.png

活动模块添加业务实现

业务描述

在活动列表页面,设计添加按钮,点击添加按钮时弹出模态框,在模态框中呈现活动添加表单元素,其添加表单原型设计,
如图所示:
image.png

业务时序分析

活动添加业务,其时序分析,如图所示:

image.png

Dao接口方法及映射定义

在ActivityDao接口中添加持久化活动信息的方法,代码如下:

int insertObject(Activity activity);

在ActivityMapper映射文件中添加映射元素,代码如下:

 <insert id="insertObject" parameterType="com.cy.pj.activity.pojo.Activity"
             useGeneratedKeys="true" keyProperty="id">
          insert into tb_activity
          (title,category,startTime,endTime,remark,state,createdUser,createdTime) 
          values    
          (#{title},#{category},#{startTime},#{endTime},
          #{remark},#{state},#{createdUser},now())
 </insert>

"#{}"表达式数据获取分析,如图所示:

image.png

执行insert操作时获取数据库主键id值,如图所示:

image.png

当然,也可以以注解的方式进行SQL映射,如图所示:

image.png

当活动创建好以后,假如到了结束时间,动态更新活动的状态,代码如下:

@Update("update tb_activity set state=0 where id=#{id}")
int updateState(Long id);

Service接口方法定义及实现

在ActivityService接口中,添加保存活动信息的方法,代码如下:

int saveActivity(Activity entity);

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

@Override
public int saveActivity(Activity entity) {
        int rows=activityDao.insertObject(entity);
        Timer timer=new Timer();
         //此对象可以负责去执行一些任务(这个对象内置一个线程和一个任务队列)
         //启动线程执行任务调度
        timer.schedule(new TimerTask() {//TimerTask为任务
            @Override
            public void run() {
                //一旦调用此任务的线程获得了CPU就会执行这个任务的run方法
                activityDao.updateState(entity.getId());
                timer.cancel();//退出任务调度(后续线程也会销毁)
            }
        }, entity.getEndTime());//按指定时间执行任务.
        return rows;
}

Controller方法定义及实现

在ActivityController中添加,处理添加请求的方法,代码如下:

@RequestMapping("/activity/doSaveActivity")
@ResponseBody
public String doSaveActivity(Activity activity) {
 activityService.saveActivity(activity);
 return "save ok";
}

Activity 页面添加表单设计及实现

在activity.html页面中添加表单元素,用于实现与用户交互,首先在activity页面上添加一个添加按钮,代码如下:

<button type="button" class="btn btn-primary btn-sm"
            data-toggle="modal" data-target="#myModal">创建活动
</button>

在指定位置添加模态框(可参考bootcss.com),代码如下:

<div class="modal fade" id="myModal" tabindex="-1" role="dialog"
            aria-labelledby="myModalLabel">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal"
                                                aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                        <h4 class="modal-title" id="myModalLabel">创建活动</h4>
                    </div>
                    <div class="modal-body">    
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default"data-dismiss="modal">Close</button>
                        <button type="button" class="btn btn-primary" onclick="doSaveObject()">
                        Save Changes</button>
                    </div>
                </div>
            </div>
</div>

在模态框”modal-body”位置,添加form表单,代码如下:

<form class="form-horizontal" th:action="@{/activity/doSaveActivity}" method="post">
<div class="form-group">
    <label for="titleId" class="col-sm-2 control-label">标题</label>
        <div class="col-sm-10">
         <input type="text" class="form-control"  name="title" id="titleId" placeholder="title">
        </div>
</div>
<div class="form-group">
<label for="categoryId" class="col-sm-2 control-label">类型</label>
    <div class="col-sm-10">
        <select id="categoryId" name="category" class="form-control">
            <option value="教育培训">教育培训</option>
            <option value="企业活动">企业活动</option>
            <option value="交友活动">交友活动</option>
        </select>
    </div>
</div>
<div class="form-group" >
<label for="startTimeId" class="col-sm-2 control-label">开始时间</label>
    <div class="col-sm-10">
       <input type="text" class="form-control form_datetime"
         name="startTime" id="startTimeId" placeholder="start time">
    </div>
</div>
<div class="form-group">
    <label for="endTimeId" class="col-sm-2 control-label">结束时间</label>
    <div class="col-sm-10">
           <input type="text" class="form-control form_datetime" 
            name="endTime" id="endTimeId" placeholder="end time">
    </div>
</div>
<div class="form-group">
    <label for="remarkId" class="col-sm-2 control-label">备注</label>
        <div class="col-sm-10">
           <textarea type="text" class="form-control"  rows="5" name="remark" id="remarkId">
            </textarea>
    </div>
</div>
</form>

添加JS代码,处理模态框Save按钮事件,代码如下:

function doSaveObject(){
        //1.定义url
        let url="/activity/doSaveObject";
        //2.定义请求参数
        var params=
      $("#saveFormId").serialize();//serialize()为jquery中直接获取表单数据方法
        console.log("params",params);
        //3.发送异步请求
        $.ajax({
            type:"post",
            url:url,
            data:params,
            success:function(result){
                alert(result);
                //隐藏模态框
                $('#myModal').modal('hide');
                //重新执行查询,局部刷新
                findActivitys();
            }
        });
        
    }

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

启动tomcat ,访问活动列表页面,如图所示:
image.png

在活动列表页面,点击添加按钮,然后启动活动添加模块框,执行添加操作如图所示:
image.png

Spring MVC 为方法参数赋值分析,如图所示:

image.png

日历控件拓展实现(开始时间和结束时间通过选择实现),效果展示,如图所示:
image.png

Bootstrap-datapicker控件的实现过程,如图所示:
image.png

项目启动及运行过程中的BUG分析

  • BindingException,如图所示:

image.png

  • 400 异常,如图所示:

image.png

  • 服务端日期格式设计,如图所示:

image.png

  • 完整性约束异常,如图所示:

image.png

总结(Summary)

本模块中讲解了活动模块中类的设计,映射设计,交互设计以及实现,然后可以基于此模块的练习加强对业务和技术上的设计及是实现。

查看原文

赞 95 收藏 20 评论 13

Jason 发布了文章 · 8月31日

07-SpringBoot+MyBatis+Spring 技术整合实现商品模块的CRUD操作

业务描述

基于Spring,MyBatis,SpringBoot,Thymeleaf技术实现商品模块的增删改查操作。

项目环境初始化

准备工作

1. MySQL(5.7)
2. JDK (1.8)
3. Maven (3.6.3)
4. STS(4.7.1)

数据库初始化

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

mysql –uroot –proot

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

set names utf8;

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

source d:/goods.sql

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

drop database if exists dbgoods;
create database dbgoods default character set utf8;
use dbgoods;
create table tb_goods(
     id bigint primary key auto_increment,
     name varchar(100) not null,
     remark text,
     createdTime datetime not null
)engine=InnoDB;
insert into tb_goods values (null,'java','very good',now());
insert into tb_goods values (null,'mysql','RDBMS',now());
insert into tb_goods values (null,'Oracle','RDBMS',now());
insert into tb_goods values (null,'java','very good',now());
insert into tb_goods values (null,'mysql','RDBMS',now());
insert into tb_goods values (null,'Oracle','RDBMS',now());
insert into tb_goods values (null,'java','very good',now());

创建项目并添加依赖

基于STS创建

第一步:基于start.spring.io 创建项目并设置基本信息

image.png

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

image.png

第三步:项目创建以后分析其结构

image.png

基于IDEA创建

第一步:基于start.spring.io 创建项目并设置基本信息

image.png

image.png
第二步:创建项目module时指定项目核心依赖

image.png

第三步:项目modul创建以后分析其结构

image.png

image.png

项目配置文件内容初始化

#server
server.port=80
#server.servlet.context-path=/
#spring datasource
spring.datasource.url=jdbc:mysql:///dbgoods?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=debug

#spring thymeleaf
spring.thymeleaf.prefix=classpath:/templates/pages/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false

项目API架构设计

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

image.png

商品查询业务实现

业务描述

从商品库查询商品信息,并将商品信息呈现在页面上,如图所示:

image.png

业务时序分析

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

Pojo类定义

定义Goods对象,用于封装从数据库查询到的商品信息。

package com.cy.pj.goods.pojo;
import java.util.Date;
public class Goods {
    private Long id;//id bigint primary key auto_increment
    private String name;//name varchar(100) not null
    private String remark;//remark text
    private Date createdTime;//createdTime datetime
    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;
    }
    @Override
    public String toString() {
        return "Goods [id=" + id + ", name=" + name + ",   
        remark=" + remark + ", createdTime=" + createdTime + "]";
    }
}

Dao接口方法及映射定义

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

package com.cy.pj.goods.dao;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import com.cy.pj.goods.pojo.Goods;

@Mapper
public interface GoodsDao {
      @Select("select * from tb_goods")
      List<Goods> findGoods();
}

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

package com.cy.pj.goods.dao;
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 GoodsDaoTests {
    @Autowired
 private GoodsDao goodsDao;
    @Test
 void testFindGoods(){
        List<Goods> goodsList=goodsDao.findGoods();
        for(Goods g:goodsList){
            System.out.println(g);
        }
    }
}

测试结果问题分析

  • 数据库连不上,如图所示:

image.png

  • 连接数据库的url 配置问题,如图所示:

image.png

  • Sql映射重复定义问题,如图所示:

image.png

  • 空指针问题,如图所示:

image.png

  • SQL语法问题,如图所示:

image.png

Service接口方法定义及实现

GoodsService接口及商品查询方法定义

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

GoodsService接口实现类GoodsServiceImpl定义及方法实现

package com.cy.pj.goods.service;
import java.util.List;
import com.cy.pj.goods.pojo.Goods;
@Service
public class GoodsServiceImpl implements GoodsService {
     @Autowired
     private GoodsDao goodsDao;
     @Override
     public List<Goods> findGoods(){
         return goodsDao.findGoods();
     }
}

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

package com.cy.pj.goods.service;
import com.cy.pj.goods.pojo.Goods;
import org.junit.jupiter.api.Assertions;
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> goodsList=goodsService.findGoods();
        //断言测试法(单元测试中常用的一种方式)
 Assertions.assertEquals(true, goodsList.size()>0);
    }
}

测试结果问题分析

  • 依赖注入问题,如图所示:

image.png

Controller对象方法定义及实现

定义GoodsController类,并添加doGoodsUI方法,添加查询商品信息代码,并将查询到的商品信息存储到model,并返回goods页面。

package com.cy.pj.goods.controller;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import com.cy.pj.goods.pojo.Goods;
import com.cy.pj.goods.service.GoodsService;
@Controller //@Service,@Component
@RequestMapping("/goods/")
public class GoodsController {
    //has a+di
    @Autowired
    private GoodsService goodsService;
    @RequestMapping("doGoodsUI")
    public String doGoodsUI(Model model) {
         //调用业务层方法获取商品信息
         List<Goods> list= goodsService.findGoods();
         //将数据存储到请求作用域
         model.addAttribute("list", list);
         return "goods";//viewname
    }
    
}

Goods商品列表页面设计及实现

在templates/pages目录中添加goods.html页面,并在body中添加html元素,在运行内部使用thymeleaf标签属性获取数据,代码如下:

<table width="50%">
        <thead>
           <th>id</th>
           <th>name</th>
           <th>remark</th>
           <th>createdTime</th>
           <th>operation</th>
        </thead>
        <tbody>
           <tr th:each="g:${list}">
             <td th:text="${g.id}">1</td>
             <td th:text="${g.name}">MySQL</td>
             <td th:text="${g.remark}">DBMS</td>
             <td th:text="${#dates.format(g.createdTime, 'yyyy/MM/dd HH:mm')}">2020/07/03</td>
             <td><a>delete</a></td>
           </tr>
        </tbody>
  </table>
thymeleaf 是一种模板引擎,此引擎以html为模板,可以添加自定义标签属性,可以将服务端model中数据填充在页面上,然后实现与用于交互。其官网为thymeleaf.org

Goods页面上数据呈现分析:

image.png

启动Tomcat进行访问测试分析

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

项目启动及运行过程中BUG及问题分析

  • STS控制台“?”符号,如图所示:

image.png

  • 服务启动失败,如图所示:

image.png

  • 模板不存在错误,如图所示:

image.png

  • 日期格式不正确,如图所示:

image.png

  • 页面上${}内容错误,如图所示:

image.png

  • 页面日期格式不正确,如图所示:

image.png

  • 依赖注入失败,如图所示:

image.png

  • 空指针异常(NullPointerException),如图所示:

image.png

商品删除业务实现

业务描述

从商品库查询商品信息后,点击页面上删除超链接,基于id删除当前行记录,如图所示:

image.png

业务时序分析

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

image.png

Dao接口方法及映射定义

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

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

 int deleteById(Integer id);

Service接口方法定义及实现

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

int deleteById(Integer id);

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

@Override
    public int deleteById(Integer id) {
        long t1=System.currentTimeMillis();
        int rows=goodsDao.deleteById(id);
        long t2=System.currentTimeMillis();
        System.out.println("execute time:"+(t2-t1));
        return rows;
    }

Controller对象方法定义及实现

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

    @RequestMapping("doDeleteById/{id}")
    public String doDeleteById(@PathVariable Integer id){
        goodsService.deleteById(id);
        return "redirect:/goods/doGoodsUI";
    }

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

Goods页面上删除超链接定义

在goods.html页面中添加删除超链接,如图所示:

image.png

Thymeleaf 官方th:href应用说明,如图所示:

image.png

删除操作中,客户端与服务端代码关联说明,如图所示:

image.png

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

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

image.png

删除成功以后,的页面如图所示:

image.png

项目启动及运行过程中的Bug及问题分析

  • SQL映射元素定义问题,如图所示:

image.png

  • 客户端请求参数与服务端参数不匹配,如图所示:

image.png

商品添加业务实现

业务描述

在Goods列表页面,添加添加按钮,进行添加页面,然后在添加页面输入商品相关信息,然后保存到数据库,如图所示:

image.png

商品添加页面,设计如图所示:

image.png

业务时序分析

在商品添加页面,输入商品信息,然后提交到服务端进行保存,其时序分析如图所示:
image.png

Dao接口方法及映射定义

在GoodsDao中添加用于保存商品信息的接口方法以及SQL映射,代码如下:

@Insert("insert into tb_goods(name,remark,createdTime) 
values (#{name},#{remark},now())")
int insertObject(Goods entity);

说明:当SQL语句比较复杂时,也可以将SQL定义到映射文件(xml文件)中。

Service接口方法定义及实现

在GoodsService接口中添加业务方法,用于实现商品信息添加,代码如下:

    int saveGoods(Goods entity);

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

    @Override
    public int saveGoods(Goods entity) {
        int rows=goodsDao.insertObject(entity);
        return rows;
    }

Controller对象方法定义及实现

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

@RequestMapping("doSaveGoods")
public String doSaveGoods(Goods entity) {
        goodsService.saveGoods(entity);
        return "redirect:/goods/doGoodsUI";
}

在GoodsController类中添加用于返回商品添加页面的方法,代码如下:

   @RequestMapping("doGoodsAddUI")
    public String doGoodsAddUI() {
        return "goods-add";
    }

Goods添加页面设计及实现

在templates的pages目录中添加goods-add.html页面,代码如下

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">
   ul li {list-style-type: none;}
</style>
</head>
<body>
<h1>The Goods Add Page</h1>
<form th:action="@{/goods/doSaveGoods}" method="post">
   <ul>
      <li>name:
      <li><input type="text" name="name">
      <li>remark:
      <li><textarea rows="5" cols="50" name="remark"></textarea>
      <li><input type="submit" value="Save">
   </ul>
</form>
</body>
</html>

在goods.html页面中添加,超链接可以跳转到添加页面,关键代码如下:

<a th:href="@{/goods/doGoodsAddUI}">添加商品</a>

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

第一步:启动web服务器,检测启动过程是否OK,假如没有问题进入下一步。
第二步:打开浏览器在地址里输入http://localhost/goods/doGood...,出现如下界面,如图所示:
image.png

第三步:在添加页面中填写表单,然后点击save按钮将表单数据提交到服务端,如图所示:
image.png
第四步:添加页面中表单数据提交过程分析,如图所示:
image.png

项目启动及运行过程中的Bug及问题分析

  • 客户端显示400异常,如图所示:

image.png

  • 保存时500异常,如图所示:

image.png

  • 数据库完整性约束异常,如图所示:

image.png

商品修改业务实现

业务描述

在商品列表页面,点击update选项,基于商品id查询当前行记录然后将其更新到goods-update页面,如图所示:
image.png
在update页面选中,修改商品信息,然后点击 update goods 将表单数据提交到服务端进行更新

业务时序分析

基于id查询商品信息的时序设计
image.png
将goods-update页面中的数据提交到服务端进行更新的时序设计
image.png

Dao接口方法及映射定义

在GoodsDao中添加基于id查询商品信息的方法及SQL映射,代码如下:

@Select("select * from tb_goods where id=#{id}")
Goods findById(Integer id);

在GoodsDao中添加基于id执行Goods商品更新的方法及SQL映射,代码如下:

 @Update("update tb_goods set name=#{name},remark=#{remark} where id=#{id}")
 int updateGoods(Goods goods);

Service接口方法定义及实现

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

Goods findById(Integer id);
int updateGoods(Goods goods);

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

    @Override
    public Goods findById(Integer id) {
        //.....
        return goodsDao.findById(id);
    }
    @Override
    public int updateGoods(Goods goods) {
        return goodsDao.updateGoods(goods);
    }

Controller对象方法定义及实现

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

    @RequestMapping("doFindById/{id}")
    public String doFindById(@PathVariable Integer id,Model model) {
        Goods goods=goodsService.findById(id);
        model.addAttribute("goods",goods);
        return "goods-update";
    }

在GoodsController中添加更新商品信息的方法,代码如下:

    @RequestMapping("doUpdateGoods")
    public String doUpdateGoods(Goods goods) {
        goodsService.updateGoods(goods);
        return "redirect:/goods/doGoodsUI";
    }

Goods修改页面设计及实现

在templates目录中添加goods-update.html页面,代码设计如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">
  ul li {list-style-type: none}
</style>
</head>
<body>
   <h1>The Goods Update Page</h1>
   <form th:action="@{/goods/doUpdateGoods}" method="post">
      <input type="hidden" name="id" th:value="${goods.id}">
      <ul>
        <li>name:
        <li><input type="text" name="name" th:value="${goods.name}">
        <li>remark:
        <li><textarea rows="3" cols="30" name="remark" th:text="${goods.remark}"></textarea>
        <li><input type="submit" value="Update Goods">
       </ul>
   </form>
</body>
</html>

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

启动tomcat服务,访问商品列表页面,如图所示:

image.png

在列表页面,点击update选项,进入更新页面

image.png

在更新页面更新表单数据,然后提交,进入列表页面查看更新结果,如图所示:

image.png

项目启动及运行过程中的BUG及问题分析

  • 页面设计分析,如图所示:

image.png

  • 页面源码分析,如图所示:

image.png

总结(Summary)

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

查看原文

赞 196 收藏 67 评论 10

Jason 发布了文章 · 8月31日

说说软件为什么要分层?

背景分析

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

软件分层设计

分层设计的本质其实就是将复杂问题简单化,首先基于单一职责原则(SRP-Single responsibility principle)让每个对象各司其职,各尽所能。然后再基于“高内聚,低耦合”的设计思想实现相关层对象之间的交互。这样可以更好提高程序的可维护性和可扩展性,例如生活中的楼宇设计,生日蛋糕设计,企业的组织架构设计等。

通讯领域的OSI参考模型

在计算机通讯领域还有典型的OSI参考模型(Open System Interconnection Reference Model),TCP/IP参考模型等,如图所示:

image

其中,TCP/IP参考模型是OSI参考模型的一种网络简化通讯模型,这里不对每一层进行介绍,具体有关OSI和TCP/IP的知识请自行进行查阅。

互联网领域的分层架构

基于Java的互联网架构,在进行分层设计时,首先要对整体的系统分层架构有一个基本认识,如图所示:

image

在上图中,很多公司会根据雇员的技能特长进行相关分工,例如有运维工程师,中间件平台开发工程师,产品开发工程师,测试工程师等。我们很多初级的java程序员主要从事的是产品开发或者是应用软件(例如客户关系管理系统,分销管理系统,配送管理系统,支付系统等)开发,而在应用软件开发时,我们一般也要进行分层设计,例如典型的MVC分层设计,我们可以参考阿里巴巴开发手册中给出分层设计模型,如图所示:

image

其中,在上图中的箭头表示一种直接依赖关系,开放接口层可以依赖于 Web 层,也可以直接依赖于 Service 层,其它依此类推(具体每层要实现的逻辑可自行查阅阿里巴巴开发手册)。

总结(Summary)

系统分层设计是一种设计思想,是让每层对象都有一个独立职责,再让多层对象协同(耦合)完成一个完整的功能。这样做可以更好提高系统可扩展性,但同时也会增加系统整体运维的难度。

查看原文

赞 53 收藏 15 评论 3

Jason 发布了文章 · 8月30日

04-SpringBoot工程下如何实现对HikariCP连接池的整合?

池化思想分析

池化思想是我们项目开发过程中的一种非常重要的思想,如整数池,字符串池,对象池、连接池、线程池等都是池化思想的一种应用,都是通过复用对象,以减少因创建和释放对象所带来的资源消耗,进而来提升系统性能。例如Integer对象的内部池应用,代码如下:

package com.cy.java.pool;
public class TestInteger01 {
    public static void main(String[] args) {
        Integer n1=100;//Integer.valueOf(100) 编译时优化
        Integer n2=100;
        Integer n3=200;
        Integer n4=200;//池中没有则new Integer(200)
        System.out.println(n1==n2);//true
        System.out.println(n3==n4);//false 
    }
    
}

数据库连接池简介

背景分析

目开发过程中应用程序与数据库交互时,“获得连接”或“释放连接”是非常消耗系统资源的两个过程,频繁地进行数据库连接的建立和关闭会极大影响系统的性能,若多线程并发量很大,这样耗时的数据库连接就可能让系统变得卡顿。因为TCP连接的创建开支十分昂贵,并且数据库所能承载的TCP并发连接数也有限制,针对这种场景,数据库连接池应运而生。如下图所示:

image.png

思考:假如现在是让你去设计一个连接池,你会从什么角度进行设计?
第一:物理存储结构(基于什么结构去存储数据)
第二:基于什么算法从池中取连接?
第三:基于什么算法从池中移除连接?
第四:当池中没有连接时,基于什么方式处理连接请求?
第五:池是可以共享,我们需要考虑池在访问的时并发安全?

连接池原理分析

在系统初始化的时候,在内存中开辟一片空间,将一定数量的数据库连接作为对象存储在对象池里,并对外提供数据库连接的获取和归还方法。用户访问数据库时,并不是建立一个新的连接,而是从数据库连接池中取出一个已有的空闲连接对象;使用完毕归还后的连接也不会马上关闭,而是由数据库连接池统一管理回收,为下一次借用做好准备。如果由于高并发请求导致数据库连接池中的连接被借用完毕,其他线程就会等待,直到有连接被归还。整个过程中,连接并不会关闭,而是源源不断地循环使用,有借有还。数据库连接池还可以通过设置其参数来控制连接池中的初始连接数、连接的上下限数,以及每个连接的最大使用次数、最大空闲时间等,也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

Java中的连接池

Java官方,为了在应用程序中更好的应用连接池技术,定义了一套数据源规范,例如javax.sql.DataSource接口,基于这个接口,很多团队或个人创建了不同的连接池对象。然后我们的应用程序中通过耦合与DataSource接口,便可以方便的切换不同厂商的连接池。Java项目中通过连接池获取连接的一个基本过程,如下图所示:

image.png

在上图中,用户通过DataSource对象的getConnection()方法,获取一个连接。假如池中有连接,则直接将连接返回给用户。假如池中没有连接,则会调用Dirver(驱动,由数据库厂商进行实现)对象的connect方法从数据库获取,拿到连接以后,可以将连接在池中放一份,然后将连接返回给调用方。连接需求方再次需要连接时,可以从池中获取,用完以后再还给池对象。

数据库连接池在Java数据库相关中间件产品群中,应该算是底层最基础的一类产品,作为企业应用开发必不可少的组件,无数天才们为我们贡献了一个又一个的优秀产品,它们有的随时代发展,功成身退,有的则还在不断迭代,老而弥坚,更有新生代产品,或性能无敌,或功能全面。目前市场上常见的连接池有DBCP、C3P0,DRUID,HikariCP等。

SpringBoot工程下HikariCP整合测试

数据初始化

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

mysql –uroot –proot

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

set names utf8;

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

source d:/goods.sql

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

drop database if exists dbgoods;
create database dbgoods default character set utf8;
use dbgoods;
create table tb_goods(
     id bigint primary key auto_increment,
     name varchar(100) not null,
     remark text,
     createdTime datetime not null
)engine=InnoDB;
insert into tb_goods values (null,'java','very good',now());
insert into tb_goods values (null,'mysql','RDBMS',now());
insert into tb_goods values (null,'Oracle','RDBMS',now());
insert into tb_goods values (null,'java','very good',now());
insert into tb_goods values (null,'mysql','RDBMS',now());
insert into tb_goods values (null,'Oracle','RDBMS',now());
insert into tb_goods values (null,'java','very good',now());

创建项目Module并添加相关依赖

第一步:基于IDEA创建项目Module,如图所示:

image.png

第二步:添加依赖
1) mysql数据库驱动依赖。

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

2) spring对象jdbc支持(此时会默认帮我们下载HiKariCP连接池)。

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

配置HikariCP连接池

打开application.properties配置文件,添加如下内容(必写)。

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

hikariCP 其它额外配置(可选),代码如下(具体配置不清晰的可自行百度):

spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1

HikariCP 连接池测试

单元测试API设计及应用分析,如图所示:

image.png

在项目中添加单元测试类及测试方法,代码如下:

package com.cy.pj.common.datasource;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DataSourceTests {
    @Autowired
    private DataSource dataSource;
    @Test
    public void testConnection() throws Exception{
        System.out.println(dataSource.getConnection());
    }
}

在当前测试类中我们需要:

  • 掌握单元测试类、测试方法编写规范。
  • 理解DataSource的设计规范及规范的实现。
  • 分析在测试类中dataSource属性指向的对象是谁?
  • 分析在测试类中DataSource的实现类对象由谁创建和管理?
  • 思考基于DataSource接口获取连接的基本过程是怎样的?

测试BUG分析

  • 数据库不存在,如图所示:

image.png

  • 类编译错误,DataSource为javax.sql包中的类型,如图所示:

image.png

  • 连接错误:数据库连接不上,如图所示:

image.png

基于HikariCP实现JDBC操作(练习)

业务分析

基于HikariCP,借助JDBC技术访问商品库中的数据。

API架构设计

基于业务,进行API设计,如图所示:

image.png

业务时序图分析

基于业务需求,进行商品查询过程的的时序图设计,如图所示:

image.png

业务代码设计及实现

第一步:定义GoodsDao接口,例如:

package com.cy.pj.goods.dao;
import java.util.List;
import java.util.Map;
/**
 * 商品模块数据访问层接口
 */
public interface GoodsDao {
    /**
 * 查询所有商品信息,将每一行记录存储到一个map对象,然后将多个存储到list集合.
 */ List<Map<String,Object>> findGoods();
}

第二步:创建GoodsDao接口实现类,代码如下:

package com.cy.pj.goods.dao;
/**
 * 此对象为一个商品数据层访问对象,现在要求在此类中定义一个方法,这个方法基于JDBC从从数据库获取商品信息,并将其封装到map集合,要求一个行记录一个map对象(key为表中字段名,值为字段名对应的值),多个map存储到list集合. @Repository此注解通常用于描述数据层实现类对象,本质上就是一个特殊的@Component, 都是要交给spring框架管理的一个Bean对象
 */
@Repository
public class DefaultGoodsDao implements  GoodsDao{
       @Autowired
       private DataSource dataSource;//hikariCP
       /**查询商品信息,一行记录映射为内存中的一个map对象*/
       public List<Map<String,Object>> findGoods(){
           Connection conn=null;//java.sql.*
           Statement stmt=null;
           ResultSet rs=null;
           String sql="select * from tb_goods";
           //1.获取连接(从连接池获取)
           try {
               conn=dataSource.getConnection();
               //2.创建statement对象
               stmt=conn.createStatement();
               //3.发送sql
               rs=stmt.executeQuery(sql);
               //4.处理结果
               List<Map<String,Object>> list=new ArrayList<>();
               while(rs.next()){//循环一次取一行,一行记录映射为一个map对象
                  list.add( rowMap(rs));//将存储了一行记录的map对象再存储到list集合
               }
               return list;
           }catch (SQLException e){
               e.printStackTrace();
               throw new RuntimeException(e);//转换为非检查异常(编译时不检测的异常)
           }finally{
               //5. 释放资源
               close(rs,stmt,conn);
           }
       }

定义行映射方法

       private Map<String,Object> rowMap(ResultSet rs)throws SQLException{
           Map<String,Object> rowMap=new HashMap<>();
           //方法1映射
           //rowMap.put("id",rs.getInt("id"));
           //rowMap.put("name",rs.getString("name"));
           //rowMap.put("remark",rs.getString("remark"));
           //rowMap.put("createdTime",rs.getTimestamp("createdTime"));
           //方法2映射
           ResultSetMetaData rsmd=rs.getMetaData();//获取元数据(包括表中的字段名)
           int columnCount=rsmd.getColumnCount();//获取列的数量
           for(int i=0;i<columnCount;i++){
               rowMap.put(rsmd.getColumnLabel(i+1),rs.getObject(rsmd.getColumnLabel(i+1)));
               //getColumnLabel(i+1);获取表中字段名或字段名对应的别名
           }
           return rowMap;
       }

定义释放资源的方法

       private void close(ResultSet rs,Statement stmt,Connection conn){
           if(rs!=null)try{rs.close();}catch(Exception e){e.printStackTrace();}
           if(stmt!=null)try{stmt.close();}catch(Exception e){e.printStackTrace();}
           //这里的连接是返回到了池中
           if(conn!=null)try{conn.close();}catch(Exception e){e.printStackTrace();}
       }
}

测试代码的编写及运行

定义单元测试类,并对其查询过程进行单元测试,例如:

package com.cy.pj.goods.dao;


@SpringBootTest
public class GoodsDaoTests {

      @Autowired
      private GoodsDao goodsDao;

      @Test
      void testFindGoods(){
          List<Map<String,Object>> list= goodsDao.findGoods();
          for(Map<String,Object> map:list){
              System.out.println(map);
          }
      }

}

测试运行过程中的BUG分析

对测试过程中出现的问题进行记录,分析,总结.

总结(Summary)

总之,数据库连接池的为我们的项目开发及运行带来了很多优点,具体如下:

  • 资源重用更佳。

由于数据库连接得到复用,减少了大量创建和关闭连接带来的开销,也大大减少了内存碎片和数据库临时进程、线程的数量,使得整体系统的运行更加平稳。

  • 系统调优更简便。

使用了数据库连接池以后,由于资源重用,大大减少了频繁关闭连接的开销,大大降低了TIME_WAIT的出现频率。

  • 系统响应更快。

数据库连接池在应用初始化的过程中一般都会提前准备好一些数据库连接,业务请求可以直接使用已经创建的连接,而不需要等待创建连接的开销。初始化数据库连接配合资源重用,使得数据库连接池可以大大缩短系统整体响应时间。

  • 连接管理更灵活。

数据库连接池作为一款中间件,用户可以自行配置连接的最小数量、最大数量、最大空闲时间、获取连接超时间、心跳检测等。另外,用户也可以结合新的技术趋势,增加数据库连接池的动态配置、监控、故障演习等一系列实用的功能。

查看原文

赞 116 收藏 39 评论 13

Jason 发布了文章 · 8月29日

01-Vue 快速入门实现?

VUE 简介

什么是VUE?

VUE是一个基于MVVM设计模式的渐进式(融合)的纯前端JS框架,基于此框架我们可以让客户端的操作更简单。

如何理解框架?
可以将其理解为已经包含部分核心功能的半成品代码,缺少个性化定制,主要用于避免重复编码实现。

什么场景使用VUE?

绝大多数以数据操作(增删改查)为主的PC端或移动端项目都可用vue开发比如: 美团,饿了么,淘宝,知乎,大众点评,微博...

VUE 快速入门

官方js文件下载

cn.vuejs.org

业务及代码实现

构建HTML页面,呈现如下效果,并在图中点击加,减按钮时实现中间数字的变化(要求基于vue技术进行实现)。

image.png

第一步:定义HTML页面并引入vue.js文件。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script data-original="/js/vue.js"></script>
</head>
<body>
</body>
</html>

第二步:在html页面中的body内部添加如下元素。

 <div id="app">
    <button @click="minus">-</button>
    <span>{{n}}</span>
    <button @click="add">+</button>
 </div>

说明:

1)在界面中要添加vue对象要监控元素,习惯上都用 id="app"
2)找到界面中将来可能发生变化的位置,用{{变量名}}特殊语法标记出来
3)找到界面中可以点击的位置绑定事件处理函数。

第三步:在html页面中的body底端部分构建JS代码实现

在JS中创建一个Vue类型的对象来监视页面中的内容。

<script>
    new Vue({
      el:"#app",
      data:{
        n:0 //起始值为0
      },
      methods:{
        add(){
          this.n++ //访问data中变量必须用this
        },
        minus(){
          if(this.n>0){ this.n-- }
        }
      }
    })
  </script>
其中:
1)el(element缩写)属性:"选择器",为Vue对象要监视的范围。
2)页面所有变量,都必须保存在data属性中,且以对象属性形式保存。
3)页面所有事件处理函数,都必须保存在methods属性中,且以对象方法形式保存。

总结(Summary)

本小节为Vue的一个快速入门案例, 基于此案例,我们要了解在页面引入vue.js时,其实是引入了一种Vue类型,如果使用vue框架做开发,必须都要创建new Vue()对象,对象创建时我们指定el,data,methods属性。然后可基于vue对象监控页面元素和事件,进而实现页面上数据的更新。

查看原文

赞 16 收藏 5 评论 0

Jason 发布了文章 · 8月29日

如何理解Spring中的@Autowired?

@Autowired 简介

@Autowired 注解用于描述类中的属性,构造方法,set方法,配置方法等,用于告诉Spring框架按照指定规则为属性注入值(DI)。

@Autowired 应用入门

基于如下API设计,进行代码实现进而分析@Autowired应用分析。
image

第一步:设计Cache接口

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

第二步: 设计Cache接口实现类WeakCache,并交给spring管理。

package com.cy.pj.common.cache;
 
import org.springframework.stereotype.Component;
 
@Component
    
public class WeakCache implements Cache{
 
}

第二步: 设计Cache接口实现类SoftCache,并交给spring管理。

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

第三步:设计单元测试类

package com.cy.pj.common.cache;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
    
public class CacheTests {
    @Autowired
    @Qualifier("softCache")
    private Cache cache;
    
    @Test
    public void testCache() {
        System.out.println(cache);
    }
}

基于单元测试,分析@Autowired应用(底层通过反射技术为属性赋值)。

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

@Autowired 应用进阶

@Autowired 注解在Spring管理的Bean中也可以描述其构造方法,set方法,配置方法等,其规则依旧是先匹配方法参数类型再匹配参数名,基于如下图的设计进行分析和实现:

image

第一步:定义SearchService类并交给Spring管理

package com.cy.pj.common.service;
@Component
public class SearchService{

  private Cache cache;
  @Autowired
  public SearchService(@Qualifier("softCache")Cache cache){
     this.cache=cache;
  }
  public Cache getCache(){
     return this.cache;
  }
}
@Qualifier可以描述构造方法参数,但不可以描述构造方法。

第二步:定义SearchServiceTests单元测试类。

package com.cy.pj.common.service;
@Component
public class SearchServiceTests{

  private SearchService searchService;
  @Test
  void testGetCache(){
     System.out.println(searchService.getCache())
  }
}
可以尝试在SearchService中添加无参构造函数和setCache(Cache cache)方法,然后使用@Autowired描述setCache方法,并基于@Qualifier注解描述setCache方法参数。分析进行注入过程。

总结(Summary)

本小节讲述了,在Spring框架中使用@Autowired注解描述类中属性、构造方法时的注入规则。然后深入理解其注入过程。

查看原文

赞 23 收藏 7 评论 0

Jason 发布了文章 · 8月28日

如何理解Spring框架中的Bean对象?

Bean对象的定义?

在Spring框架中由Spring创建和管理的对象的对象称之为Bean对象。

Bean对象的特性?

Spring框架为了更加科学的管理和应用Bean对象,为其设计相关特性,例如:懒加载、作用域以及生命周期方法。

懒加载(Lazy)

在Spring框架中,默认会在启动时会创建所有的Bean对象,但有些bean对象假如长时间不用,启动时就创建对象,会占用其内存资源,从而造成一定的资源浪费,此时我们可以基于懒加载策略延迟对象的创建,在设计类时,在类上添加@Lazy注解,例如:

package com.cy.pj.common.pool;
@Component
@Lazy
public class ObjectPool {
  public ObjectPool() {
    System.out.println("ObjectPool()");
  }
}
其中,@Lazy注解中有一个value属性,其默认值为true表示延迟加载。

作用域(Scope)

Spring框架创建对象以后,可以给对象一个作用域,目的是让对象在一定范围内可以进行重用,常用有单例(singleton)和多例(prototype)两种,其中:

  • singleton:此作用域指的是,名字和类型相同的Bean对象实例在整个Spring容器中只能一份。此实例何时创建与类的延迟加载特性配置有关,此实例创建以后,生命周期会由spring框架管理。
  • prototype:此作用域指的是,每次从Spring容器获取对象都会创建新实例,此实例会在需要时创建与lazy特性无关,这个实例创建以后,spring可以对其初始化,但不负责销毁。

基于注解@Scope方式设定Bean作用域,代码演示:

package com.cy.pj.common.pool;、
@Lazy
@Scope("singlton")
@Component
public class ObjectPool {
  public ObjectPool() {
    System.out.println("ObjectPool()");
  }
}
Spring中默认bean对象的作用域为singleton,假如希望是prototype可以使用@Scope("prototype")

生命周期方法

程序中每个对象都有生命周期,但不是每个对象类型定义时,都要定义生命周期方法,当一个Bean对象创建以后假如还需执行额外初始化,销毁之前也需要额外的其它资源的释放,Spring框架可以为类定义其生命周期方法。假如是注解方式,我们可以采用如下两个注解进行实现。

  • @PostConstruct 注解用于描述bean对象生命周期方法中的初始化方法,此方法会在对象的构造方法之后执行(是对象创建以后的初始化)。
  • @PreDestroy 注解用于描述Bean对象生命周期方法中的销毁方法,此方法会在对象销毁之前执行(当作用域为prototype时,此方法不会执行)。

代码演示:

package com.cy.pj.common.pool;、
@Lazy
@Scope("singlton")
@Component
public class ObjectPool {
  public ObjectPool() {
    System.out.println("ObjectPool()");
  }
  @PostConstruct 
  public void init() {
    System.out.println("init()");
  }
  @PreDestroy
  public void close() {
    System.out.println("close()");
  }
}
一般池对象都会设置一些生命周期方法,例如连接池。

总结(Summary)

本小节主要对Spring框架中的Bean对象特性做了一个概要分析,可以通过这个分析了解Spring框架是如何科学应用bean对象的。

查看原文

赞 22 收藏 8 评论 0

Jason 发布了文章 · 8月28日

SpringBoot 入门案例的实现?

业务描述

在SpringBoot工程中,快速实现Bean对象的创建,配置和测试。

API设计分析

在这个入门案例中,以DefaultCache类(一个普通的类)作为设计和应用入口进行分析和实现,例如:

image

在上图中描述了DefaultCacheTests类与DefaultCache类的关系,这两个类通过指定注解(@SpringBootTest,@Component)进行了描述,其意图是告诉spring框架这个两个类的实例的创建由Spring负责,并且由Spring框架基于@Autowired注解的描述完成DefaultCacheTests实例中有关DefaultCache类型的值的注入(DI)。

代码设计及实现

第一步:创建一个DefaultCache类,存储到src/main/java目录,然后交给spring管理。

package com.cy.pj.common.cache;
@Component
    
public class DefaultCache {}
@Component是Spring中用于描述Bean类的一个注解。用于告诉Spring这框架个类的实例由Spring创建,当此对象由Spring创建和管理时,默认会将对象存储到池(Bean池)中。

第二步:添加sringboot 测试类,进行bean的获取及测试,要放在src/test/java目录中:

package com.cy.pj.common.cache;
@SpringBootTest
public class DefaultCacheTests {// is a Object
    @Autowired
    private DefaultCache defaultCache;//has a
    @Test
    public void testCache() {
         //use a system
        System.out.println(defaultCache);
    }
}
@SpringBootTest 注解用于告诉spring框架,此测试类交给spring管理。
@Autowired注解描述属性时,用于告诉spring框架要为此属性注入一个值?(至于注入规则,后面课程慢慢加强)

运行BUG分析

image

image

image

总结(Summary)

本小节对SpringBoot工程下类的编写,配置和测试做了一个基本实现。
重点在如何基于API设计进行代码的实现和测试。

查看原文

赞 9 收藏 5 评论 0

Jason 发布了文章 · 8月27日

如何理解JAVA中的泛型?

泛型简介

什么是泛型?

  • 参化类型,数是JDK1.5的新特性。(定义泛型时使用参数可以简单理解为形参),例如List<E>,Map<K,V>
  • 编译时的一种类型,此类型仅仅在编译阶段有效,运行时无效.例如List<String>在运行时String会被擦除,最终系统会认为都是Object.

为什么要使用泛型?

泛型是进行类型设计或方法定义时的一种约束规范,基于此规范可以:

  1. 提高编程时灵活性(有点抽象,后续结合实例理解)。
  2. 提高程序运行时的性能。(在编译阶段解决一些运行时需要关注的问题,例如强转)
说明:泛型应用相对比较简单,难点在泛型类或方法的设计上,通过这样的设计如何对现有类进行减法设计,提高类或方法的通用性.

泛型应用类型

泛型定义时常用方式有三种(可参考List<E>,Map<K,V)等接口定义):

  1. 泛型类:class 类名<泛型,…>{}
  2. 泛型接口:interface 接口名<泛型,…>{}
  3. 泛型方法: <泛型> 方法返回值类型 方法名(形参){}

泛型类定义及应用

类泛型定义:(用于约束类中方法参数和方法返回值类型)

class Array<T>{//类泛型:类名<泛型>
    Object[] array=new Object[10];
    public void add(T t){}//通过类泛型约束方法参数类型
    public T get(int i){//通过类泛型约束方法返回值类型
        return (T)array[i];
    }
}

泛型接口定义及应用

定义接口时指定泛型,用于约束接口方法参数类型以及方法返回值类型

interface Task<Param,Result>{//思考map中的泛型Map<K,V>
    /**
     * 此方法用于执行任务
     * @param arg 其类型由泛型参数Param决定
     * @return 其类型由泛型参数result决定
     */
    Result execute(Param arg);
}

泛型接口应用实践

class ConvertTask implements Task<String,Integer>{
    @Override
    public Integer execute(String arg) {
        // TODO Auto-generated method stub
        return Integer.parseInt(arg);
    }
}

泛型定义及应用

泛型方法中的泛型仅仅应用于当前方法,主要应用于一些静态方法,当然非静态方法也可以应用。

class ObjectFactory{
    /**泛型方法*/
    public static <T>T newInstance(Class<T> cls){
        return (T)cls.newInstance();
    }
}

泛型应用通配符应用

无界限定通配符

泛型无界通配符使用”?”进行表示,可以代表一种任意参数类型(实参类型)。一般应用于变量的定义。例如:Class<?> c1;

  Class<?> c=Class.forName("java.util.Date");

上届界限定通配符

泛型上届限定符通过“<? extends 类型>”方式进行实现,主要用于方法参数或方法的返回值类型,例如:

static void doPrint(List<? extends CharSequence> list){
        System.out.println(list);
}

下届限定通配符

泛型下届限定符通过“<? super 类型>”方式进行实现,主要用于方法参数或方法的返回值类型,例如:

static void doPrint(Set<? super Integer> set){
        System.out.println(list);
}

泛型类型擦除实践分析

泛型是编译时的一种类型,在运行时无效,运行时候都会变成Object类型,例如:

ArrayList<String> list = new ArrayList<String>();
list.add("A");
//list.add(100);//直接不可以
//通过反射将100添加到集合
list.getClass().getMethod("add", Object.class).invoke(list, 100);
System.out.println(list);

总结(Summary)

本小节对泛型的定义,应用场景,应用类型以及泛型的类型擦出进行了分析和实践,希望在后续项目实践中对泛型有一个更好的认识。

查看原文

赞 15 收藏 7 评论 0

Jason 发布了文章 · 8月25日

如何访问Google在线文档?

Google在线文档简介

Google在线文档支持在线创建、编辑和共享文档,基于这种方式,我们可以在团队间进行实时协作,可以更快速地完成工作。使用免费且非常便捷。

Google访问助手安装

我们在国内往往不能直接访问Google文档,可以借助一些第三方插件或者代理进行访问,例如Google访问助手。

第一步:下载Google访问助手。

自己可通过搜索引擎进行查找,或者从https://chrome.zzzmh.cn/这里...

第二步:安装Google访问助手,如图所示:
打开浏览器的扩展程序选项进行安装
image

安装成功以后的效果,如图所示:

image

Google 在线文档访问

打开浏览器输入docs.google.com进行访问,会出现如下视图:

image

说明:假如只是访问他人的文档,账号可以不创建。

Google 之外的其它在文档

在这种线上办公的推动下国内也涌现出了很多的在线文档平台,例如:

https://docs.qq.com
https://www.yuque.com/
https://shimo.im/

这些平台也都是一些很好的在线办公平台。

总结(Summary)

本小节,主要是介绍了一下Google在线文档以及访问,线上办公是一种趋势,尝试基于线上平台实现文档的创建,编辑和共享能够在团队内部或团队间进行更好的协同,提高其工作效率。

查看原文

赞 6 收藏 5 评论 3

Jason 发布了文章 · 8月24日

SpringBoot项目实践过程中遇到过哪些问题?

SpringBoot 常见问题分析

image

image

image

image

image

image

image

image

image

image

image

HikariCP 常见问题分析

image

image

image

image

image

image

image

image

image

image

MyBatis常见问题分析

image

image

image

image

image

image

image

image

image

image

Spring 常见问题分析

image

image

image

image

image

image

image

image

image

image

image

image

image

image

image

image

image

image

image

thymeleaf 常见问题分析

image

image

image

image

image

image

image

Ajax 常见问题分析

image

image

Summary

本小节主要希望提高同学们对问题的思考,分析以及解决问题的能力,并对问题进行总结,沉淀,形成经验和习惯。

查看原文

赞 138 收藏 67 评论 5

Jason 发布了文章 · 8月22日

如何理解JavaScript中的对象?

JavaScript中对象的简介

我们知道生活中客观存在的一切事物皆为对象,那在程序中的对象是什么样子呢?我们可以将程序中的对象理解为客户端世界中的对象在一种计算机中的一种表示方式.所有的编程语言中提到的对象其性质都是类似的,它往往对应内存中的一块区域,在这个区域中存储对象的属性或方法信息。

JavaScript中对象的创建

基于{}符号创建对象

在JS中我们可以直接基于{}定义对象,在对象内容定义属性和方法,例如:
image
在JS中我们可以将对象理解为用于封装属性和方法的一个结构体,例如
image

如果需要反复创建多个相同结构的对象时,用{}代码就很冗余——重复。也不便于维护!

基于构造函数创建对象

假如在JS中我们希望非常方便的定义多个结构相同,但属性值不同的对象,可以直接基于构造函数( 定义同一类型多个对象的相同属性结构的特殊函数)进行对象创建,例如:
image

建议:只要反复创建多个相同结构的对象,都要用构造函数来创建对象.

在实际应用中我们尽量不要在构造函数中再去定义函数,例如:

image

假如构造函数中再定义函数,会在构建对象时,为构造函数内部的函数开辟额外的函数空间。

JavaScript中的对象特性

封装特性

JS中的对象用于封装属性和方法.如图所示:
image

继承特性

JS中为了实现属性或方法的可重用性,提供了一种继承机制,这种继承的方式需要借助Prototype对象去实现。Prototype对象是在定义构造函数时自动创建,用于存储所有共有属性和方法的一个对象,所有通过此构造函数构建的对象都继承这个原型对象.
构造函数中的原型对象分析,如图所示:

image

原型对象(ProtoType)对象的继承,如图所示:
image

在对象中添加共有属性,如图所示:

image

在对象中添加共有方法的.如图所示:
image

基于JS原型对象同时添加多个共有方法,如图所示:
image

原型链是由多级父对象(原型对象)逐级继承形成的链式结构。这个原型链保存着一个对象可以访问的所有父级原型对象,以及这个对象可访问到的所有属性和方法。

JS中我们定义的所有构造函数,默认只想的原型对象为Object类型中的原型对象,如图所示:

image.png

多态特性

多态一般指同一个函数(行为),在不同情况下表现出的不同的状态。从应用上有两种形式,分别为重载和重写。
重载的定义:一个函数,根据传入的实参值不同,执行不同的逻辑。
重写的定义:在子对象中定义一个和父对象中成员同名的成员。只要从父对象继承来的东西不好用,就用重写自己的!

在子对象中定义一个和父对象中成员同名的自有成员,例如:
image

JavaScript内置对象类型

对象类型简介

JavaScript中对象的类型可以理解为构造方法和原型对象的结合体,当我们需要查看其对象类型时,可以通过构造方法名.

JS中内置的对象类型

JS中有11种内置对象类型,分别为String, Number, Boolean, Array, Date, RegExp, Math(对象), Error, Function, Object, global(对象)

总结(Summary)

本小节主要是对JS中对象做了简介,并通过实践方式分析JS中对象的创建,对象特性以及对象内置类型。

查看原文

赞 89 收藏 36 评论 2

Jason 发布了文章 · 8月9日

01-如何理解微服务(Microservice)?

背景分析

单体应用架构

在传统的单体应用系统架构中,一般分为三个部分,即数据库端、服务应用端和前端展现端,如图所示:
image
在业务发展初期,由于所有的业务逻辑在一个应用中,开发、测试、部署都比较容易。但是,随着业务的发展,系统为了应对不同的业务需求会不断为单体应用增加不同的业务模块。久而久之,不断扩充的业务需求导致单体应用的系统越来越庞大臃肿。此时,单体应用的问题也逐渐显现出来,由于单体应用是一个“整体”,往往修改一个小的功能,为了部署上线就会影响到其他功能的运行。

对于业务而言,往往不同模块对系统资源的要求不也尽相同,而单体应用各个功能模块因为无法分割,也就无法细化对系统资源的需求。所以,单体应用在初期是比较方便快捷,但是随着业务的发展,维护成本会越来越大,且难以控制。

云原生应用分析

云原生的概念最早开始于2010年,主要用于描述一种和云一样的系统行为应用的编写,比如分布式的、松散的、自服务的、持续部署与测试的。当时提出“云原生”是为了能构建一种符合云计算特性的标准来指导云计算应用的编写。

导致了“云原生”概念的诞生的因素,主要有几个方面:
  • 云计算平台的普及。
  • 虚拟化技术的发展。
  • 敏捷开发的出现,DevOps的应用缩短了发布周期

云平台具有很好的可扩展性,为了利用这一点,云原生应用由多个小而独立的模块组成,如图所示:

image

基于这些独立模块构建的服务架构,就叫做微服务。

微服务简介

微服务定义

微服务架构(MSA)的基础是将单个应用程序开发为一组小型独立服务,这些独立服务在自己的进程中运行,独立开发和部署。
image

image
这些服务使用轻量级 API 通过明确定义的接口进行通信。这些服务是围绕业务功能构建的,每项服务执行一项功能。由于它们是独立运行的,因此可以针对各项服务进行更新、部署和扩展,以满足对应用程序特定功能的需求。

微服务是分布式系统中的一种流行的架构模型,它并不是银弹,所以,也不要寄希望于微服务构架能够解决所有的问题。微服务架构主要解决的是如何快速地开发和部署我们的服务,这对于一个能够适应快速开发和成长的公司是非常必要的。同时,微服务设计中有很多很不错的想法和理念,通过学习微服务架构我们可以更快的迈向卓越。

微服务特性

  • 自主性

可以对微服务架构中的每个组件服务进行开发、部署、运营和扩展,而不影响其他服务的功能。这些服务不需要与其他服务共享任何代码或实施。各个组件之间的任何通信都是通过明确定义的 API 进行的。

  • 专用性

每项服务都是针对一组功能而设计的,并专注于解决特定的问题。如果开发人员逐渐将更多代码增加到一项服务中并且这项服务变得复杂,那么可以将其拆分成多项更小的服务。

微服务的优势

  • 敏捷性

微服务促进若干小型独立团队形成一个组织,这些团队负责自己的服务。各团队在小型且易于理解的环境中行事,并且可以更独立、更快速地工作。这缩短了开发周期时间。您可以从组织的总吞吐量中显著获益。

  • 扩展性

通过微服务,您可以独立扩展各项服务以满足其支持的应用程序功能的需求。这使团队能够适当调整基础设施需求,准确衡量功能成本,并在服务需求激增时保持可用性。

  • 轻松部署

微服务支持持续集成和持续交付,可以轻松尝试新想法,并可以在无法正常运行时回滚。由于故障成本较低,因此可以大胆试验,更轻松地更新代码,并缩短新功能的上市时间。

  • 代码可重用性

将软件划分为小型且明确定义的模块,让团队可以将功能用于多种目的。专为某项功能编写的服务可以用作另一项功能的构建块。这样应用程序就可以自行引导,因为开发人员可以创建新功能,而无需从头开始编写代码。

  • 较好的弹性设计

服务独立性增加了应用程序应对故障的弹性。在整体式架构中,如果一个组件出现故障,可能导致整个应用程序无法运行。通过微服务,应用程序可以通过降低功能而不导致整个应用程序崩溃来处理总体服务故障。

总结(Summary)

在本章节中,讲述了微服务诞生的背景,传统单体架构的劣势,同时,基于云原生技术的的发展,微服务架构诞生。基于微服务架构,可以更好的,灵活的处理客户端的请求并提高系统的可靠性,可扩展性。

查看原文

赞 8 收藏 5 评论 0

Jason 发布了文章 · 8月9日

如何理解JavaScript中的闭包设计?

背景分析

在讲闭包之前我们要先回顾一下JS中的全局变量和局部变量的作用域特性。

JS中的全局变量都有不可兼得的优缺点。
  • 全局变量:可重用性好,任意地方都可使用,但极易被污染。
  • 局部变量:仅函数内可用,不易被污染,但可重用性不好。
JS中全局与局部变量案例分析:

image

思考:有没有一种方式既可以保证变量的可重用性又不被污染呢?

JS中的闭包简介

JS中的闭包设计,是一种设计思想,其主要目的是保证一个函数内部的变量既可以得到重用,又不被污染(不会被随意篡改)。

JS中闭包设计及应用

基本步骤分析
  1. 定义外部函数,用于封装要保护的变量以及可外部访问的内部函数。
  2. 定义外部函数中受保护的变量以及用于操作保护变量的的内部函数并将内部函数返回。
  3. 调用外部函数获取内部函数,然后基于内部函数操作受保护变量。
代码设计过程
function outer(){
   var count=0;//这个变量外部不可直接使用(可理解为受保护变量)
   return function(){
     count++; //通过内部函数操作受保护变量
     console.log(count);
   }
}

var inner=outer();//调用外部函数获取内部函数
inner();调用内部函数操作受保护变量.
案例分享(简易支付设计)

image

总结(Summary)

JavaScript中的闭包设计是结合JS中变量作用域的特性,给出的一种既要保护变量,又要重用变量的设计方案。类似生活中孩子的压岁钱交给父母管理,孩子需要时从父母那进行支取的设计方式。

查看原文

赞 17 收藏 10 评论 0

Jason 发布了文章 · 8月9日

如何理解JavaScript中的函数?

JS中的函数简介

JS中的函数是一种通过调用来完成具体业务的一段代码块。最核心的目的是将可重复执行的操作进行封装,然后供调用方无限制的调用。

JS中的函数的定义

JS中函数定义,有如下两种形式:

  • 方式1
function f1(){} //函数声明,f1为函数名,可以将其理解为变量f1指向一个函数
function f2(){return 100;}//函数允许有返回值
function f3(a,b){}//函数中可以定义多个参数,无需指定变量类型
  • 方式2:
var f4=function(){} //函数表达式,这里变量名f4为函数名
var f5=function(){return 100;}//函数允许有返回值
var f6=function(a,b,c){}
案例分享

image

JS中的函数调用

JS中的函数定义好以后,必须调用才会执行,常用调用方式有如下两种:

  • 方式1:通过函数名直接调用
f1();
f3(10,20); //调用函数时指定其实际参数
f6(10,20,30)
  • 方式2:匿名函数自调用,目的是创建函数作用域,防止污染全局.
(function(){})()
(function(){}())
案例分享

基于函数名进行调用
image

image

函数的自调用
image

回调函数

JS中的回调函数,将函数以实参的形式进行传递。

function f(callback){
 callback()//调用传入的匿名函数
};//定义函数
f(function(){console.log("hello"))//调用函数,函数内部传入的匿名函数为回调函数
案例分享

image

总结(Summary)

本小节主要从函数的概念,定义,调用,回调几个角度对函数做了一个基本介绍,小试伸手,用于交流。

查看原文

赞 80 收藏 29 评论 9

Jason 发布了文章 · 7月29日

02-如何基于STS实现SpringBoot 工程项目的创建?

SpringBoot项目环境初始化

本次项目的创建,需要如下软件已准备并已完成其配置.

  • 下载、安装、配置JDK(这里选择JDK8版本)。
  • 下载、解压、配置maven(这里选择3.6.3版本)
  • 下载、解压、配置STS(这里选择4.7.1版本)

说明:对于环境下载,安装配置,自己通过官网或搜索引擎查询实现。

SpringBoot项目创建过程分析及实现

基于STS内置start创建

第一步:打开sts软件,快捷键ctrl+n打开新建窗口,如图所示:

image.png

第二步:输入项目相关信息,如图所示:

image.png

第三步:选择SpringBoot版本,如图所示:

image.png

第四步:项目结构展示,项目创建完以后的结构如下:

image.png

基于浏览器(Browser)创建

第一步:打开浏览器输入start.spring.io,然后输入项目信息,进行项目构建,如图所示:

image.png

第二步:将构建并下载下来的项目进行解压,如图所示:

image.png

第三步:将解压好的项目导入到sts中,如图所示:

image.png

image.png

第四部步:项目结构展示,如图所示:

image.png

基于STS普通maven方式创建

第一步:创建普通maven项目,快捷键ctrl+n 搜maven,如图所示:

image.png

第二步:选择项目骨架,如图所示:

image.png

第三步:输入像项目信息,如图所示:

image.png

第四步:项目结构展示,如图所示:

image.png

第五步:将已有项目的pom文件,项目启动类,配置文件拷贝到maven工程中,并进行适当修改,例如修改一下pom文件中项目名称.

SpringBoot项目创建过程问题分析

JDK安装配置问题

本地安装的JDK缺少资源,如图所示:

image.png

Maven配置及更新问题

Maven的配置文件错误,如图所示:

image.png

Maven中不能有多个私服镜像,如图所示

image.png

Maven项目依赖下载更新问题

image.png

网络连接(Connect)问题

spring 官方服务器连接超时.

image.png

总结(Summary)

本小节重点讲解了基于STS工具创建和导入SpringBoot工程的几种方式以及在实践过程的一些问题的思考和解决方案。

查看原文

赞 58 收藏 14 评论 0

Jason 发布了文章 · 7月28日

01-如何理解 Spring Boot技术?

Spring Boot 背景分析

JAVAEE应用体系中繁重的配置、低下的开发效率、高难度的三方集成,复杂的部署流程等等一直被开发人员所诟病。即使是使用Spring这样的轻量级的资源整合框架,在实现其相对比较多的资源整合时,依旧需要大量的手动依赖管理,复杂的XML配置(还经常没有提示)。还有就是现在的软件生态应用也已经形成一定的规模,系统架构正在从单体架构,分布式架构,跨越到微服务架构。随着整个架构体系的变化,企业对技术的要求也在变化,现在的企业更注重技术的开箱即用,更注重技术在生态圈中的深度融合,更注重轻量级的运维。由此由此spring boot诞生。

Spring Boot 要解决什么问题

Spring Boot是由Pivotal团队提供的全新的Java软件开发框架(很多人现在把它理解为一个脚手架),其设计目的是用来简化Spring项目的初始搭建以及开发过程。该框架使用了特定的注解方式来进行配置,从而使开发人员不再需要大量的xml配置。不再需要大量的手动依赖管理。Spring Boot基于快速构建理念,通过约定大于配置,开箱即用的方式,希望能够在蓬勃发展的快速应用开发领域成为其领导者。

Spring Boot 有哪些核心的关键特性

  • 起步依赖(Starter Dependency)。
  • 自动配置(Auto Configuration)。
  • 健康检查(Actator)-监控。
  • 嵌入式服务(Tomcat,Jetty)。

总结(Summary)

总之,Spring Boot 框架就是要基于快速构建理念,基于约定大于配置方式,实现技术的开箱即用,以提高开发效率。

查看原文

赞 80 收藏 20 评论 6