产品功能的实现:
1.首先实现软件的功能;
2.学会控制;
3.运维(项目运行时日志的分析,项目的布署,项目的拓展)
1. 项目简介
1.1概述
动吧旅游生态系统,应市场高端用户需求,公司决定开发这样的一套旅游系统,此系统包含旅游电商系统(广告子系统,推荐子系统,评价子系统,商品子系统,订单子系统,…),旅游分销系统(分销商的管理),旅游业务系统(产品研发,计调服务,系统管理,…
1.2原型分析
基于用户需求,进行原型设计(基于html+css+js进行静态页面实现)。例如系统登录页面:(bootstrap)
系统登录成功页面(例如starter.html)
菜单展示页面
说明:原型设计好以后,会与客户进行确认,确认好以后进行签字,然后就是设计和实现.
2.技术架构
2.1项目分层架构
本项目应用层基于MVC设计思想,进行分层架构设计,其核心目的是将复杂问题简单化,实现各司其职,各尽所能.然后基于“高内聚,低耦合”的设计思想,再实现各对象之间协同,从而提高系统的可维护性,可扩展性。
其中:
1.开放接口层:可直接封装 Service 方法暴露成 RPC (远程过程调用)接口;也可通过 Web 封装成 http 接口;同时也可进行网关安全控制、流量控制等。
2.终端显示层:负责各个端的模板渲染并显示。当前主要是 thymeleaf 渲染,JS 渲染,移动端展示等。
3.Web请求处理层:主要是对访问控制进行转发,请求参数校验,响应结果处理等
4.Service 层:相对具体的业务逻辑服务层(核心业务,扩展业务)。
5.Manager 层:通用业务处理层,它有如下特征:
1) 对第三方平台封装的层,预处理返回结果及转化异常信息;
2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理;
3) 与 DAO 层交互,对多个 DAO 的组合复用。
6.DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。
7.外部接口或第三方平台:包括其它部门RPC开放接口,基础平台,其它公司的 HTTP 接口
说明:对如上分层中涉及到知识的点,逐步加强。
总之:分层的目的就是将复杂问题进行拆解,然后分而治,进而提高系统的可扩展性以及可维护性。
2.2API应用架构
整体API应用架构:
## 3.技术整合
3.1 环境整备
3.1.1数据库初始化
启动MySQL客户端并登陆,然后执行
1)set names utf8;
2)source d:/jtsys.sql
说明:假如在mysql客户端查询表中数据,可以先执行set names gbk,否则可能会出现乱码。
3.1.2 配置初始环境
1.统一工作区编码(UTF-8)
2.统一JDK版本(JDK1.8)
3.统一MAVEN配置(3.6.3)
创建项目
1.项目名称:CGB-DB-SYS-V1.01
2.组ID: com.cy
3.打包方式:jar
3.1.3添加依赖
3.1.4修改配置文件(application.yml文件)
a)
#server
server:
port: 80
servlet:
context-path: /
tomcat:
threads:
max: 512
#spring
spring:
datasource:
url: jdbc:mysql://local host:3307/d'bsys?serverTimezone=GMT
username: root
password: root
thymeleaf:
prefix: classpath:/templates/pages/
suffix: .html
#mybatis
mybatis:
configuration:
default-statement-timeout: 30
map-underscore-to-camel-case: true
mapper-locations:
- classpath:/mapper/*/*.xml
#lOG
logging:
level:
com.cy: DEBUG
b)application.properties
server.port=8080
server.tomcat.threads.max=512
spring.datasource.url=jdbc:mysql://localhost:3307/dbactivity?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
mybatis.mapper-locations=classpath:/mapper/*/*.xml
spring.thymeleaf.prefix=classpath:/templates/pages/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
logging.level.com.cy=debug
3.2首页初始化
3.2.1定义页面初始资源
1.将js、css、images相关资源拷贝到项目static目录
2.将pages页面拷贝到项目的templates目录
3.3创建包结构及相关文件
测试:
package com.cy.pj.sys.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 项目中所有页面的处理都放在这个controller中
*/
@Controller
@RequestMapping("/")
public class PageController {
@RequestMapping("doIndexUI")
public String doIndexUI(){
return "starter";
}
}
启动tomcat,在地址栏输入http://localhost/doIndexUI(地址中的端口号要参考自己tomcat启动端口)地址进行访问,假如没有问题会呈现如下页面:
AdminLTE基于bootstramp实现(官网:AdminLTE.io)
layui,easyui
用户行为日志管理
1.业务设计说明
本模块主要是实现对用户行为日志(例如谁在什么时间点执行了什么操作,访问了哪些方法,传递的什么参数,执行时长等)进行记录、查询、删除等操作。其表设计语句如下:
CREATE TABLE `sys_logs` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`operation` varchar(50) DEFAULT NULL COMMENT '用户操作',
`method` varchar(200) DEFAULT NULL COMMENT '请求方法',
`params` varchar(5000) DEFAULT NULL COMMENT '请求参数',
`time` bigint(20) NOT NULL COMMENT '执行时长(毫秒)',
`ip` varchar(64) DEFAULT NULL COMMENT 'IP地址',
`createdTime` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='系统日志';
2.原型设计说明
基于用户需求,实现静态页面(html/css/js),通过静态页面为用户呈现基本需求实现,如图
3.API设计说明
业务时序分析
4.服务端实现
加载顺序
PageController中
//在PageController中定义返回日志列表
@RequestMapping("log/log_list")
public String doLogUI() {
return "sys/log_list";
}
//在PageController中定义用于返回分页页面
@RequestMapping("doPageUI")
public String doPageUI() {
return "common/page";
}
starter.html页面:
<script type="text/javascript">
$(function(){
doLoadUI("load-log-id","log/log_list")
})
function doLoadUI(id,url){
$("#"+id).click(function(){//click事件处理函数
//jquery中load函数为一个异步加载的ajax函数
//此函数用于在指定位置异步加载资源(并将返回的资源填充到id为mainContentId的div中)
$("#mainContentId").load(url);
/* $.get(url,function(result){
$("#mainContentId").html(result);
},"html") */
})
}
</script>
或者改为
log_list.html中:
<script type="text/javascript">
$(function(){
//当日志列表加载完成后,加载分页页面
$("#pageId").load("doPageUI");
})
</script>
加载成功后页面
pojo中创建SysLog类
package com.cy.pj.sys.pojo;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
*此对象主要用于封装数据库提取的数据或者向数据库写入的数据
*此对象中的属性建议和表中字段有对应的映射关系(名字,类型)
建议:所有用于封装数据的对象都建议实现序列化接口Serializable
1)序列化:将对象转换为字节的过程称之为对象序列化
2)反序列化:将字节转换为对象的过程称之为反序列化
应用场景:对对象进行缓存,将对象进行钝化(写入文件),将对象通过网络进行传输
*/
@Data
public class SysLog implements Serializable{
private static final long serialVersionUID = -1592163223057343412L;
//对象在序列化和反序列化的时候会基于此id进行数据处理
//将对象序列化时会将这个id作为版本号写入到字节中
//反序列化时会从字节中提取版本id然后和类中id进行对比,一致进行反序化
//private static final long serialVersionUID = 1L;
private Integer id;
//用户名
private String username;
//用户操作
private String operation;
//请求方法
private String method;
//请求参数
private String params;
//执行时长(毫秒)
private Long time;
//IP地址
private String ip;
//创建时间
private Date createdTime;
}
创建SysLogDao文件
@Mapper
public interface SysLogDao {
/**
* 基于条件查询用户行为日志记录总数
* @param username 查询条件
* @return 查询到的记录总数
*
* */
int getRowCount(@Param("username")String userName);
/**
* 基于条件查询当前页记录
* @param username 查询条件
* @param startIndex 查询时的起始位置(用于limit子句)
* @param pageSize 当前页面大小(每页最多显示多少条记录)
* @return 查询到的记录
*
* */
public List<SysLog> findPageObjects( @Param("username")String username,
@Param("startIndex")Integer startIndex,
@Param("pageSize")Integer pageSize );
}
创建mapper映射文件(SysLogMapper.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.pj.sys.dao.SysLogDao">
<select id="findPageObjects" resultType="com.cy.pj.sys.pojo.SysLog">
select * from sys_logs
<where>
<if test="username!=null and username!=''">
username like concat("%",#{username},"%")
</if>
</where>
order by createdTime
limit #{startIndex},#{pageSize}
</select>
<select id="getRowCount" resultType="int">
select count(*) from sys_logs
<where>
<if test="username!=null and username!=''">
username like concat("%",#{username},"%")
</if>
</where>
</select>
</mapper>
提取共性代码
<mapper namespace="com.cy.pj.sys.dao.SysLogDao">
<sql id="queryWhereId">
from sys_logs
<where>
<if test="username!=null and username!=''">
username like concat("%",#{username},"%")
</if>
</where>
</sql>
<select id="findPageObjects" resultType="com.cy.pj.sys.pojo.SysLog">
select *
<include refid="queryWhereId"/>
order by createdTime
limit #{startIndex},#{pageSize}
</select>
<select id="getRowCount" resultType="int">
select count(*)
<include refid="queryWhereId"/>
</select>
</mapper>
创建测试类SysLogDaoTests
package com.cy.pj.sys.dao;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.cy.pj.sys.entity.SysLog;
@SpringBootTest
public class SysLogDaoTests {
@Autowired
private SysLogDao sysLogDao;
@Test
public void testGetRowCount() {
int rows=sysLogDao.getRowCount("admin");
System.out.println("rows="+rows);
}
@Test
public void testFindPageObjects() {
List<SysLog> list= sysLogDao.findPageObjects("admin", 0, 3);
for(SysLog log:list) {
System.out.println(log);
}
}
}
学习网站
pojo:
1)do(data object):数据层封装数据,要求与数据库表结构有一一对应关系
2)bo(business object):在业务层封装业务执行结果,由Service层输出的封装业务逻辑对象
3)vo(view object):显示层对象,通常是web像模板渲染引擎层传输的数据.例如:model
对应关系:
do bo vo
dao service controller
创建PagaObject类
package com.cy.pj.common.pojo;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
/**
* 基于此对象封装业务执行结果
*
* 在Java语言,可以简单将内存中的对象分为两大类:
* 1)存储数据的对象(设计的关键在属性上)——典型的pojo对象(VO,BO,DO)
* 2)执行业务的对象(设计的关键在方法上)——典型的controller,service,dao
*/
@Data
public class PageObject<T> implements Serializable {//pojo中的bo对象
private static final long serialVersionUID = -1696088982496489016L;
/** 当前页的页码值 (用户点击以后,客户端传到服务端)*/
private Integer pageCurrent = 1;
/** 页面大小 (每页最多可以呈现的记录总数)*/
private Integer pageSize = 3;
/** 总行数(通过数据库查询获得) */
private Integer rowCount = 0;
/** 总页数(通过计算获得 基于rowCount和页面大小计算出来) */
private Integer pageCount = 0;
/** 当前页记录 List<T>中T由PageObject类上定义的泛型T决定*/
private List<T> records;
public Integer getPageCurrent() {
return pageCurrent;
}
public PageObject(Integer pageCurrent, Integer pageSize, Integer rowCount, List<T> records) {
super();
this.pageCurrent = pageCurrent;
this.pageSize = pageSize;
this.rowCount = rowCount;
/*
* //计算总页数方法一: this.pageCount=rowCount/pageSize; if(rowCount%pageSize!=0) {
* pageCount++; }
*/
//计算总页数方法二:
this.pageCount=(rowCount-1)/pageSize+1;
this.records = records;
}
创建接口SysLogService
package com.cy.pj.sys.service;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysLog;
public interface SysLogService {
/**
* @param name 基于条件查询时的参数名
* @param pageCurrent 当前的页码值
* @return 当前页记录+分页信息
*/
PageObject<SysLog> findPageObjects(String userName,Integer pageCurrent);
}
创建接口实现类SysLogServiceImpl
package com.cy.pj.sys.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.dao.SysLogDao;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
/**
*
*/
@Service
public class SysLogServiceImpl implements SysLogService {
@Autowired
private SysLogDao sysLogDao;
@Override
public PageObject<SysLog> findPageObjects(String userName, Integer pageCurrent) {
// TODO Auto-generated method stub
/*
* 验证参数合法性 1.验证pageCurrent的合法性 不合法抛出IllegalArgumentException异常
*/
if (pageCurrent == null || pageCurrent < 1) {
throw new IllegalArgumentException("当前页码不正确");
}
/*
* 2. 基于条件查询总记录数并校验 2.1)执行查询
*/
int rowCount = sysLogDao.getRowCount(userName);
if (rowCount == 0) {
throw new ServiceException("系统没有查到对应记录");
// 为了对业务中的信息进行更好的反馈和定位,通常会在项目中自定义异常
// throw new RuntimeException("记录不存在");//尽量避免抛出RuntimeException
}
/*
* 3.基于pageCurrent条件查询当前页记录(pageSize定义为2) 3.1)定义pageSize
*/
int pageSize = 2;// 每页最多显示多少条记录
// 3.2)计算startIndex
int startIndex = (pageCurrent - 1) * pageSize;
// 3.3)执行当前数据的查询操作
List<SysLog> records = sysLogDao.findPageObjects(userName, startIndex, pageSize);
/*
* 4.对分页信息以及当前页记录进行封装并返回 4.1)构建PageObject对象
*
*
* PageObject<SysLog> pageObject=new PageObject<>(); //4.2)封装数据
* pageObject.setPageCurrent(pageCurrent);
*
* pageObject.setPageSize(pageSize);
*
* pageObject.setRowCount(rowCount);
*
* pageObject.setRecords(records); if(rowCount%pageSize!=0) pageCount++;
* pageObject.setPageCount((rowCount-1)/pageSize+1);
*
* //5.返回封装结果 return pageObject;
*/
return new PageObject<>(pageCurrent, pageSize, rowCount, records);
}
}
自定义secvice异常,ServiceException类,继承RuntimeException,生成序列化id
创建com.cy.pj.common.exception包,创建ServiceException
package com.cy.pj.common.exception;
/**
* 自定义非检查异常
* 目的:对业务中的信息进行更好的反馈和定位
* 说明:此类中的构造方法是基于父类RuntimeException
*/
public class ServiceException extends RuntimeException{
private static final long serialVersionUID = 7793296502722655579L;
public ServiceException() {
super();
// TODO Auto-generated constructor stub
}
public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public ServiceException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public ServiceException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
说明:几乎在所有的框架中都提供了自定义异常,例如MyBatis中的BindingException等。
定义Service对象的单元测试类
package com.cy.pj.sys.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysLog;
@SpringBootTest
public class SysLogServiceTests {
@Autowired
private SysLogService sysLogService;
@Test
public void testFindPageObjects() {
PageObject<SysLog> pageObject=
sysLogService.findPageObjects("admin", 1);
System.out.println(pageObject);
}
}
Controller类的实现
定义控制层值对象(VO),目的是基于此对象封装控制层响应结果(在此对象中主要是为业务层执行结果添加状态信息)。Spring MVC框架在响应时可以调用相关API(例如jackson)将其对象转换为JSON格式字符串。
package com.cy.pj.common.pojo;
import java.io.Serializable;
import lombok.Data;
/**
* 基于此对象封装服务端要相应到客户端的数据,这个数据包含:
* 1)状态码(表示这个响应结果是正确的还是错误的)
* 2)状态信息(状态码对象对应的状态消息)
* 3)正常的响应数据(例如一个查询结果)
*
* POJO:(vo---view Object:封装了视图层(表现层)要呈现的数据)
*/
@Data
public class JsonResult implements Serializable{
private static final long serialVersionUID = 5110901796917551720L;
//状态码,默认状态为ok
private Integer state=1;
//状态信息
private String message="ok";
//正常的响应数据
private Object data;
public JsonResult(String message){
this.message=message;
}
public JsonResult(Object data){
this.data=data;
}
public JsonResult(Throwable e){//Throwable 为所有异常类父类
this.state=0;
this.message=e.getMessage();
}
public JsonResult() {}
}
定义Controller类,并将此类对象使用Spring框架中的@Controller注解进行标识,表示此类对象要交给Spring管理。然后基于@RequestMapping注解为此类定义根路径映射。
package com.cy.pj.sys.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.cy.pj.common.pojo.JsonResult;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
@Controller
@RequestMapping("/log/")
public class SysLogController {
@Autowired
private SysLogService sysLogService;
@RequestMapping("doFindPageObjects")
@ResponseBody
public JsonResult doFindPageObjects(String username,Integer pageCurrent){
PageObject<SysLog> pageObject=sysLogService.findPageObjects(username,pageCurrent);
//System.out.println(pageObject);
//控制层对业务层数据再次进行封装
JsonResult jr=new JsonResult();
jr.setData(pageObject);
return new JsonResult(pageObject);//此位置的业务封装为正常数据
}
}
定义全局异常处理类,对控制层可能出现的异常,进行统一异常处理,代码如下:
package com.cy.pj.common.web;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.cy.pj.common.pojo.JsonResult;
/**
* @ControllerAdvice注解描述的类,springmvc会认为它是控制层
* 全局异常处理对象
*/
@ControllerAdvice
public class GlobalExceptionHanderler {
/**
* @ExceptionHandler 描述的方法为异常处理的方法
* 此注解中定义的异常类型,为这个方法可以处理的异常类型,它可以处理
* 此异常以及这个异常类型的子类类型的异常
* @param e
* @return
* */
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public JsonResult doHandleRuntimeException(RuntimeException e) {
e.printStackTrace();
return new JsonResult(e);
}
}
客户端页面事件分析:
当用户点击首页日志管理时,其页面流转分析如图:
日志列表信息呈现:
log_list.html中:
$(function() {
/* //当日志列表加载完成后,加载分页页面
$("#pageId").load("doPageUI", function() {//页面或者资源加载完成后执行
//异步加载用户行为日志信息
doGetObjects();
}); */
$("#pageId").load("doPageUI",function(){doGetObjects();});
//上面形式简写形式
//$("#pageId").load("doPageUI",doGetObjects);//不要写成doGetObjects();
})
//开启ajax异步任务,按条件加载用户行为日志信息
function doGetObjects() {
//1.定义url和参数
var url = "log/doFindPageObjects"
//1.1定义参数的方式:
//var params="pageCurrent=1";
//1.2定义参数方式
//var params={"pageCurrent" : 1};
var params = {
"pageCurrent" : 1
};//pageCurrent=2
//2.发起异步请求
//请问如下ajax请求的回调函数参数名可以是任意吗?//可以,必须符合标识符的规范
$.getJSON(url, params, function(result) {
//请问result是一个字符串还是json格式的js对象?对象
//debugger;//断点调试
doHandleQueryResponseResult(result);
});//特殊的ajax函数
}
//如上方法简写形式
//$.getJSON(url, params, doHandleQueryResponseResult);
function doHandleQueryResponseResult(result) { //JsonResult
console.log(result);
console.log("result.state",result.state);
console.log("result.data",result.data);
console.log("result.data.records",result.data.records);
if (result.state == 1) {//ok,正常的响应数据
//更新table中tbody内部的数据
doSetTableBodyRows(result.data.records);//将数据呈现在页面上
//更新页面page.html分页数据
//doSetPagination(result.data); //此方法写到page.html中
} else {
alert(result.message);
}
}
function doSetTableBodyRows(records) {
//1.获取tbody对象,并清空对象
var tBody = $("#tbodyId");
tBody.empty();
//2.迭代records记录,并将其内容追加到tbody
for ( var i in records) {
//2.1 构建tr对象
var tr = $("<tr></tr>");
//2.2 构建tds对象
var tds = doCreateTds(records[i]);
//2.3 将tds追加到tr中
tr.append(tds);
//2.4 将tr追加到tbody中
tBody.append(tr);
}
}
function doCreateTds(data) {
var tds = "<td><input type='checkbox' class='cBox' name='cItem' value='"+data.id+"'></td>"
+
"<td>" + data.username + "</td>" +
"<td>" + data.operation + "</td>" +
"<td>" + data.method + "</td>" +
"<td>" + data.params + "</td>" +
"<td>" + data.ip + "</td>" +
"<td>" + data.time + "</td>";
return tds;
}
参数取出代码理解:
显示如图:
分页数据的呈现
日志信息列表初始化完成以后初始化分页数据(调用setPagination函数),然后再点击上一页,下一页等操作时,更新页码值,执行基于当前页码值的查询。
在page.html中:
第一步:在page.html页面中定义doSetPagination方法(实现分页数据初始化,在log_list.html中调用),代码如下:
function doSetPagination(page) {//pageObject {rowCount:10,pageCount:3,...}
//1.初始化总记录数;
$(".rowCount").html("总记录数(" + page.rowCount + ")");
//2.初始化总页数
$(".pageCount").html("总页数(" + page.pageCount + ")");
//3.初始化当前页码值
$(".pageCurrent").html("当前页(" + page.pageCurrent + ")");
//4.存储page信息到指定对象上。
//data函数为jquery中的一个数据绑定函数,其语法为data(key[,value]);,假如只有key表示取值,两个都有表示值的绑定
$("#pageId").data("pageCurrent", page.pageCurrent);
$("#pageId").data("pageCount", page.pageCount);
}
第二步:第二步:分页页面page.html中注册点击事件。代码如下:
$(function(){
//事件注册
$("#pageId").on("click",".first,.pre,.next,.last",doJumpToPage);
})
第三步:定义doJumpToPage方法(通过此方法实现当前数据查询)
function doJumpToPage(){
//1.获取点击对象的class值
var cls=$(this).prop("class");//Property
//2.基于点击的对象执行pageCurrent值的修改
//2.1获取pageCurrent,pageCount的当前值
var pageCurrent=$("#pageId").data("pageCurrent");
var pageCount=$("#pageId").data("pageCount");
//2.2修改pageCurrent的值
if(cls=="first"){//首页
pageCurrent=1;
}else if(cls=="pre"&&pageCurrent>1){//上一页
pageCurrent--;
}else if(cls=="next"&&pageCurrent<pageCount){//下一页
pageCurrent++;
}else if(cls=="last"){//最后一页
pageCurrent=pageCount;
}else{
return;
}
//3.对pageCurrent值进行重新绑定
$("#pageId").data("pageCurrent",pageCurrent);
//4.基于新的pageCurrent的值进行当前页数据查询
doGetObjects();
}
第四步:修改page.html分页查询方法:(看黄色底色部分)
列表页面信息查询实现:
当用户点击日志列表的查询按钮时,基于用户输入的用户名进行有条件的分页查询,并将查询结果呈现在页面。
第一步:日志列表页面log_list.html加载完成,在查询按钮上进行事件注册。代码如下:
$(".input-group-btn").on("click",".btn-search",doQueryObjects)
第二步:定义查询按钮对应的点击事件处理函数。代码如下:
function doQueryObjects(){
//为什么要在此位置初始化pageCurrent的值为1?
//数据查询时页码的初始位置也应该是第一页
$("#pageId").data("pageCurrent",1);
//为什么要调用doGetObjects函数?
//重用js代码,简化jS代码编写。
doGetObjects();
}
第三步:在分页查询函数中追加name参数定义(看黄色底色部分),代码如下:
第四步:将page.html中响应结果方式改为:
格式分析:
注册点击事件:
//注册事件
//on函数用于在指定html元素上注册事件,当点击html元素内部的子元素时可以触发事件
$(function() {
//$(".pagination").on("click", ".first,.pre,.next,.last", doJumpToPage)
$("#pageId").on("click", ".first,.pre,.next,.last", doJumpToPage)
})
doJumpToPage()中方法的编写:
prop函数(property缩写):
//1.获得被点击对象的class属性值,基于此值判定被点击的对象
//prop(property) 函数为jquery中用于获取class属性值的一个函数,其语法为prop(key[,value]);prop函数中假如只有key表示只取值,有key和value表示为属性赋值
//1.1获取被点击对象 var clickObj=$(this);
//1.2 获取点击对象的class属性的值 var cls=clickObj.prop("class")
var cls = $(this).prop("class");
问题分析:
1.SAXParseException
2.No tests found with runner ‘JUnit 5’
3.ParameterResolutionException
4.无法找到对应的Bean对象(NoSuchBeanDefinitionException),如图:
问题分析:
1)检测key的名字写的是否正确。
2)检测spring对此Bean对象的扫描,对于dao而言。
3)使用有@Mapper注解描述或者在@MapperScan扫描范围之内。
4)以上都正确,要检测是否编译了。
5.绑定异常(BindingException),如图:
问题分析:
1)接口的类全名与对应的映射文件命名空间不同。
2)接口的方法名与对应的映射文件元素名不存在。
3)检测映射文件的路径与application.properties或者application.yml中的配置是否一致。
4)以上都没有问题时,检测你的类和映射文件是否正常编译。
6.反射异常(ReflectionException),如图:
问题分析:
1)映射文件中动态sql中使用的参数在接口方法中没有使用@Param注解修饰
2)假如使用了注解修饰还要检测名字是否一致。
说明:当动态sql的参数在接口中没有使用@Param注解修饰,还可以借助_parameter这个变量获取参数的值(mybatis中的一种规范)。
7.结果映射异常,如图
图-16
问题分析:getRowCount元素可能没有写resultType或resultMap。
8.绑定异常
问题分析:绑定异常,检测findPageObjects方法参数与映射文件参数名字是否匹配.检查版本号是否为最新版本,不是最新版本,参数传入要加@params(" ")
9.Bean创建异常,如图
问题分析:应该是查询时的结果映射对的类全名写错了
10.请求方式不匹配,如图
问题分析:请求方式与控制层处理方式不匹配。
11.响应结果异常,如图
问题分析:服务端响应数据不正确,例如服务端没有注册将对象转换为JSON串的Bean
12.请求参数异常
问题分析:客户端请求参数中不包含服务端控制层方法参数或格式不匹配
13.JS编写错误,如图
问题分析:点击右侧VM176:64位置进行查看
问题分析:找到对应位置,检测data的值以及数据来源
问题分析:找到对应位置,假如无法确定位置,可排除法或打桩,debug分析。
问题分析:调用length方法的对象有问题,可先检测下对象的值
问题分析:检测record定义或赋值的地方。
资源没找到
问题分析:服务端资源没找到,检查url和controller映射,不要点击图中的jquery。
14.视图解析异常,如图
问题分析:检查服务端要访问的方法上是否有@ResponseBody注解.
重难点分析:
1.日志管理整体业务分析与实现。
1)分层架构(应用层MVC:基于spring的mvc 模块)。
2)API架构(SysLogDao,SysLogService,SysLogController)。
3)业务架构(查询,删除,添加用户行为日志)。
4)数据架构(SysLog,PageObject,JsonResult,…)。
2.日志管理持久层映射文件中SQL元素的定义及编写。
1)定义在映射文件”mapper/sys/SysLogMapper.xml”(必须在加载范围内)。
2)每个SQL元素必须提供一个唯一ID,对于select必须指定结果映射(resultType)。
3)系统底层运行时会将每个SQL元素的对象封装一个值对象
(MappedStatement)。
3.日志管理模块数据查询操作中的数据封装。
1)数据层(数据逻辑)的SysLog对象应用(一行记录一个log对象)。
2)业务层(业务逻辑)PageObject对象应用(封装每页记录以及对应的分页信息)。
3)控制层(控制逻辑)的JsonResult对象应用(对业务数据添加状态信息)。
4.日志管理控制层请求数据映射,响应数据的封装及转换(转换为json 串)。
1)请求路径映射,请求方式映射(GET,POST),请求参数映射(直接量,POJO)。
2)响应数据两种(页面,JSON串)。
5.日志管理模块异常处理如何实现的。
1)请求处理层(控制层)定义统一(全局)异常处理类。
2)使用注解@ControllerAdvice描述类,使用@ExceptionHandler描述方法.
3)异常处理规则:能处理则处理,不能处理则抛出。
FAQ分析
用户行为日志表中都有哪些字段?(面试)
用户行为日志是如何实现分页查询的?(limit)
用户行为数据的封装过程?(数据层,业务层,控制层)
项目中的异常是如何处理的?
页面中数据乱码,如何解决?(数据来源,请求数据,响应数据)
说说的日志删除业务是如何实现?
Spring MVC 响应数据处理?(view,json)
项目你常用的JS函数说几个?(data,prop,ajax,each,…)
MyBatis中的@Params注解的作用?(为参数变量其别名)
Jquery中data函数用于做什么?可以借助data函数将数据绑定到指定对象,语法为data(key[,value]),key和value为自己业务中的任意数据,假如只有key表示取值。
Jquery中的prop函数用于获取html标签对象中”标准属性”的值或为属性赋值,其语法为prop(propertyName[,propertyValue]),假如只有属性名则为获取属性值。
Jquery中attr函数为用户获取html标签中任意属性值或为属性赋值的一个方法,其语法为attr(propertyName[,propertyValue]),假如只有属性名则为获取属性值。
日志写操作事务的传播特性如何配置?(每次开启新事务)?
日志写操作为什么应该是异步的?
Spring 中的异步操作如何实现?
Spring 中的@Async如何应用?
项目中的BUG分析及解决套路?