SpringBoot+Mybatis 开发流程,以及旅游项目的知识点

1. 项目分析

首先,分析项目可能用到哪些类型的数据。本项目涉及的数据包括:日志、菜单、角色、用户。

然后,分析这些数据相关模块的开发顺序,优先开发基础数据和简单数据对应的模块。

基础数据:不以数据为前提,或者是其他数据的前提
简单数据:字段少,逻辑清晰,与其他表关联少

上述数据模块的开发顺序:日志 -> 菜单 -> 角色 -> 用户

接下来,针对一类数据,考虑都需要实现哪些功能,可以从CRUD的方向去考虑。
例如日志数据:添加日志,日志列表,删除日志

然后,决定一类数据相关功能的开发顺序,一般按照从简单到复杂顺序,常见的顺序是“增 -> 查 -> 删 -> 改”

日志数据相关功能开发顺序:日志列表 -> 删除日志 -> 添加日志

针对每一个功能,开发顺序是:创建数据表-> 创建实体类 -> 持久层 -> 业务层 -> 控制器层 -> 前端页面

POJO

POJO是各种实体类的统称,常用的实体类包括DO/DTO/BO/VO。

  1. DO:与数据库表相对应的实体类,属性与表中字段相对应(常用)
  2. DTO:业务层传输数据时使用的实体类
  3. BO:业务层对外提供远程调用时使用的实体类
  4. VO:供视图层使用的实体类(常用)

分页

分页就是将全部数据分成多页进行展示,每次仅展示1页的内容

分页的实现思路:在这里插入图片描述

优化细节1:

当SpringMVC将JsonResult转换成JSON字符串时,值为null的属性默认也会参与转换。可以在application.propertis中声明一行属性spring.jackson.default-property-inclusion=non-null,指定null值属性不再参与JSON字符串的转换。

将starter.html设为项目首页

将starter.html设为项目首页,就是在用户访问http://localhost:8080/时,为用户返回pages/starter.html的内容。在Controller中开发一个方法

	@Controller
  	public class PageController{
	@RequestMapping("/")
	public String findStarterPage(){
		return "starter";
	}
}

src/main/resources/application.properties中添加如下配置:

spring.mvc.view.prefix=/pages/
spring.mvc.view.suffix=.html

启动项目,访问http://localhost:8080/,查看是否能够正确显示项目首页。

页面内部引用了一些过时的css文件,所以在开发者工具视图下,可能出现css文件的404错误 ,只要页面可以正常显示,这些404可以忽略。

jQuery发送AJAX请求的方法

  1. $.ajax(…):设置完整,还可以添加响应异常时的处理逻辑
  2. $.get(url,data,callBack):发送get请求,编码简介
  3. $.post(url,data,callBack):发送post请求,编码简介
  4. $.getJSON(url,data,callBack):专为请求JSON数据设计,底层使用get请求
  5. $(“选择器”).load(url,data,function):会自动将收到的响应内容添加到选择器选择的组件的内部,特别适合进行子页面的动态加载。

REST风格:

url=localhost:8080/log/findSysLog/1/tom
url=localhost:8080/log/find/1/tom
url=localhost:8080/log/del/1/tom

对于REST风格的API,后台就需要从请求URL中截取目标位置的内容,作为请求参数。基于SpringMVC的@PathVariable可以非常简单的解决这一问题。

Controller中开发一个方法,响应用户对所有系统管理子页面的请求,返回对应的子页面内容:

@RequestMapping("/sys/{subPage}")
public String getSubPage(@PathVariable("subPage")String subPage){
	return "sys/"+subPage;
}

面试题:SpringMVC能否从请求url中截取参数?

答案:可以利用@PathVariable实现。在Controller方法上添加的@RequestMapping注解中,使用{变量名}的方式,将url中指定位置的值作为变量。在对应方法的参数中,声明一个接收该变量值的参数,前面使用@PathVariable("变量名")标注。

Spring AOP

概念

AOP,指面向切面编程(Aspect Oriented Programming),是一种编程思想,在实际应用中是对OOP的有效补充。

在OOP中,模块化的核心单元是类,在AOP中,模块化的核心单元是切面(Aspect),切面中封装了具体的代码。

AOP有什么用?

应用程序中的处理逻辑可以分为两类:核心关注点 和 横切关注点。

核心关注点指某项业务的核心处理逻辑。

横切关注点指那些会被多个业务重复调用,但是和具体业务关系不大的模块,例如日志模块,性能统计模块,事务管理模块,安全验证模块等。

AOP可以将横切关注点的内容封装在Aspect内部,并注入到所需的地方,有效实现核心关注点和横切关注点的解耦,提高了程序的可扩展性和可维护性,提高了开发效率。
在这里插入图片描述

如何使用:

Spring的IOC为AOP提供了强大的支持,利用Spring,可以非常便捷的实现AOP编程:

  1. 需要在项目中添加aspectj-toolsaspectjweaver的依赖:

    aspectj aspectj-tools 1.0.6 org.aspectj aspectjweaver
  2. 需要开发一个切面类cn.sd.db.common.aop.TimerAspect,在类上添加2个必要的注解@Aspect@Component:

    @Aspect
    @Component
    public class TimerAspect {

    }

  3. 在类中添加切面方法:

方法的参数列表中必须添加参数ProceedingJoinPoint,它代表了目标方法的句柄:

@Around("execution(* cn.sd.db.sys.service.impl.*.*(..))")
public Object a(ProceedingJoinPoint pjp) throws Throwable {
	
	// 记录开始时间
	long st=System.currentTimeMillis();
	
	Object result=pjp.proceed();
	
	// 记录结束时间
	long et=System.currentTimeMillis();
	
	// 输出耗时
	System.err.println(pjp.getSignature().getName()+"-> 耗时:"+(et-st)+"ms.");
	
	return result;
}

pjp.proceed()代表调用了目标方法,该目标方法可能是有返回值的方法,对于这类方法,应该接收方法的返回值,并在切面结束时返回该返回值。

pjp.proceed()调用目标方法时,可能抛出异常Throwable,如果在切面方法中不需要对异常进行处理,可直接在签名中声明抛出。

需要在切面方法前添加@Around注解,指明该切面方法是在目标方法调用前和调用后都有逻辑执行,对应的也可以添加@Before@After,但是一般没有必要。

@Around注解后需要指明当前切面方法注入的目标位置,@Around("execution(* cn.sd.db.sys.service.impl.*.*(..))")代表业务层所有的方法都被注入。

* `execution()`为表达式的主体
* 第一个"*"号表示返回值的类型任意
* `cn.sd.db.sys.service.impl`表示AOP所切的服务的包名
* 第二个"*"表示类名,*即所有类
* `.*(..)`表示任何方法名,括号表示参数,两个点表示任何参数类型

AOP中的一些基本概念:

Aspect(切面): Aspect声明类似于Java中的类声明,在Aspect中会包含着一些Pointcut以及相应的Advice。

Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

Advice(通知):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after和around来区别是在每个joint point之前、之后还是代替执行的代码。

Target(目标对象):织入 Advice 的目标对象。

Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

AOP proxy: 由AOP框架创建的对象,用于实现方面协定(建议方法执行等)。 在Spring框架中,AOP代理将是JDK动态代理或CGLIB代理。
在这里插入图片描述

在业务层获取HttpServletRequest对象

根据正常的三层架构思维,不应该在业务层操作任何的控制器层的特有对象,如request,response,session等等,这样会造成强耦合。

但是总有一些业务无法完全做到上述描述。

例如,事务管理在JDBC的API中是通过Connection对象的API实现的,但是事务管理的控制是在业务层完成的,基于原生的API,业务层必须操作持久层的特殊对象-Connection,这也是一种强耦合。

上述场景,一般通过第三方工具提供的特殊类,实现一定程度上的解耦。例如事务管理是通过Spring提供的TransactionManager实现解耦。

在业务层获取控制器层的request对象,也可以通过Spring提供的一个工具RequestContextHolder来实现,API如下:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

面试题1:什么是AOP?说说你对它的理解

  1. AOP,是面向切面编程(Aspect Oriented Programming),是一种编程思想,在实际应用中是对OOP的有效补充。
  2. 应用程序中的处理逻辑可以分为两类:核心关注点 和 横切关注点。
  3. 横切关注点指那些会被多个业务重复调用,但是和具体业务关系不大的模块,例如日志模块,性能统计模块,事务管理模块,安全验证模块等。
  4. AOP可以将横切关注点的内容封装在Aspect内部,并注入到所需的地方。有效实现核心关注点和横切关注点的解耦,提高了程序的可扩展性和可维护性,提高了开发效率。

面试题2:你在项目中是如何实现AOP的?

  1. 在项目中可以利用Spring来实现AOP编程
  2. Spring的IOC为AOP提供了强大的支持,利用Spring,可以非常便捷的实现AOP编程
  3. 在权限管理子系统中的日志模块里面,有个记录日志的用例,是使用AOP注入到用户相关业务的Service层方法中,实现自动的日志记录。

面试题3:使用Spring 具体如何实现AOP编程?

  1. 需要在项目中添加aspectj-toolsaspectjweaver的依赖
  2. 开发一个切面类,类前添加@Aspect@Component
  3. 在类中添加通知(advice)方法,在方法中添加横切关注点的处理逻辑
  4. 在通知方法前添加注解@Around("..."),配置通知注入的方式和位置

面试题4:Spring 是如何实现AOP的切面注入的?

  1. Spring是基于JDK动态代理和CBLIB的动态代理来实现的
    在这里插入图片描述

面试题5:JDK动态代理和CBLIB的动态代理的区别?

前端数据可视化插件-ECharts

https://www.echartsjs.com/zh/index.html

  1. 需要获取echarts.min.js文件

  2. 在页面中使用<script src="...">引入echarts.min.js文件

  3. 在页面的<body>标签中声明一个<div>,作为echarts图表的容器,后续生成的图表会自动显示在该div的内部。通过设置该div的样式,就可以设置图表显示的大小,和在页面中的位置

  4. 基于ECharts的API,实现图表的显示

     第一步:初始化一个图表对象
     var myChart = echarts.init(document.getElementById('main'));
     // echarts是ECharts API的内置对象,封装了常用的方法
     // init方法用于初始化图表对象,并且绑定图表显示的div
    
     第二步:声明一个图表的配置对象
     var option={
     	title: // 用于设定图表的标题
         tooltip: // 用于设定点击图表数据时显示的提示信息
     	legend: // 设定图例
     	xAxis: // 设定图表X轴显示的数据
        	yAxis: // 设定图表y轴显示的数据,由于y轴可能同时显示多组数据,因此yAxis的值一般使用{},在后面的series属性中对y轴数据进行详细的设置,但是yAxis属性不能删除
         series: [{ // 对y轴显示的数据进行配置,每个js对象是一组数据
     		name: // 数据的名称,
     		type: // 数据显示的类型,
     		data: // y轴实际显示的数据值
         }]
     }
    
     // 第三步:调用图表对象的setOption()方法,绑定图表的配置
     myChart.setOption(option); // 应用配置显示图表
    

使用插件的标准步骤:

  1. 引入插件对应的代码文件,在java中就是.jar文件,在js中就是.js文件
  2. 基于插件设计的API进行编程,具体参考插件提供的API文档

zTree前端插件

菜单节点列表是通过zTree这个前端插件实现的。使用插件的步骤:

  1. 引入插件的支持文件(已完成)
  2. 按照插件API声明对应的配置 zTree, setting
  3. 按照插件API,初始化zTree

对于任何一个新的第三方插件,开发者最初都是不会的,开发者应该具备读API文档,并找到解决方案的能力。zTree的API文档url为http://www.treejs.cn/v3/api.php

具体代码如下:

$(function(){
	doLoadZtreeNodes();
});

var zTree; //zTree是第三方扩展的一个Jquery插件
//初始化zTree时会用到
var setting = {
	check:{
		enable:true,
		chkboxType:{"Y":"s","N":"s"}
	}	
	,data : {
		simpleData : {
			enable : true,
			idKey : "id",  //节点数据中保存唯一标识的属性名称
			pIdKey : "parentId",  //节点数据中保存其父节点唯一标识的属性名称
			rootPId : null  //根节点id
		}
	}
}

//加载zTree菜单
function doLoadZtreeNodes(){
	//1.url
	var url="menu/findMenuNode";
	//2.request
	$.getJSON(url,function(result){
	 	if(result.state==20){
			zTree=$.fn.zTree.init(
						$("#menuTree"),
						setting,
						result.data);
		}else{
			alert(result.message);
		}
	});
}

Mybatis

<?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="cn.sd.db.sys.mapper.SysLogMapper">

	<sql id="queryWhereId">
		from sys_logs 
		<where>	<!-- 下面的if判断不通过的时候,where不会写入 -->
			<if test="username!=null and username!=''">
				username like concat("%",#{username},"%")		
			</if>
		</where>
	</sql>
	
	<!-- 插入日志记录 -->
	<!-- int insertSysLog(SysLogDO sysLogDO) -->
	<insert id="insertSysLog">
		insert into sys_logs
			(username, operation,
			method, params,
			time, ip,
			createdTime
			) values(
			#{username}, #{operation},
			#{method}, #{params},
			#{time}, #{ip},
			#{createdTime}
			)
	</insert>


	<!-- 基于条件删除日志记录 -->
	<!-- int deleteSysLog(@Param("ids")Integer[] ids) -->
	<delete id="deleteSysLog">
		delete from
			sys_logs
		where 
			id 
		in
		<foreach collection="ids" 
			open="(" close=")"
			separator="," item="id"
		>
			#{id}
		</foreach>
	</delete>
	
	<!-- 基于条件查询日志记录条数 -->
	<!-- int getRowCount(@Param("username")String username) -->
	<select id="getRowCount" resultType="int">
		select count(*) 
		<include refid="queryWhereId"></include>
	</select>
	
	<!-- 基于条件查询一页的日志数据 -->
	<!-- List<SysLogDO> listSysLog(@Param("username")String username,
							@Param("recordIndex")int recordIndex,
							@Param("pageSize")int pageSize); -->
	<select id="listSysLog" resultType="cn.sd.db.sys.pojo.SysLogDO">
		select  * 
		<include refid="queryWhereId"></include> 
		order by createdTime desc
		limit #{recordIndex},#{pageSize}
	</select>


	<!-- 基于菜单id查询菜单信息 -->
	<!-- SysMenuDO getSysMenu(Integer id) -->
	<select id="getSysMenu" resultType="cn.sd.db.sys.pojo.SysMenuDO">
		select 
			t1.*, t2.name as parentName
		from 
			sys_menus t1 left join sys_menus t2
		on
			t1.parentId = t2.id 
		where 
			t1.id=#{id}
	</select>

</mapper>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值