py-18-PROJECT4(下篇)

编程入门 行业动态 更新时间:2024-10-27 04:33:24

day04:京淘项目-权限管理子系统-角色模块

1.    角色列表页面呈现                                                2

1.1.    服务端实现                                                       2

1.1.1.    Controller实现                                               2

1.2.    客户端实现                                                       3

1.2.2.    定义角色列表页面                                         3

1.2.3.    首页页面菜单事件处理                                  3

1.3.    角色分页div内容加载                                        4

2.    角色列表数据呈现                                               4

2.4.    服务端实现                                                      4

2.4.4.    Entity类实现                                                 4

2.4.5.    Dao接口实现                                               5

2.4.6.    Mapper实现                                                6

2.4.7.    Service层对象                                             7

2.4.8.    Controller 实现                                            9

2.5.    客户端实现                                                    10

2.5.9.    角色页面数据呈现                                      10

2.5.10.    列表页面role_list.html信息查询实现         12

3.    角色删除模块实现                                            14

3.6.    服务端实现                                                   14

3.6.11.    DAO实现                                                14

3.6.12.    Mapper实现                                           15

3.6.13.    Service实现                                           16

3.6.14.    Controller实现                                       17

3.7.    客户端实现                                                17

3.7.15.    角色列表页面删除操作实现                  17

4.    角色添加页面呈现                                         18

4.8.    服务端实现                                                18

4.8.16.    角色Controller对象的实现                    18

4.9.    客户端实现                                               19

4.9.17.    角色列表添加按钮事件处理                 19

4.9.18.    角色添加页面菜单信息呈现                 20

5.    角色添加操作实现                                        21

5.10.    服务端实现                                             21

5.10.19.    Dao实现                                            21

5.10.20.    Mapper实现                                      22

5.10.21.    Service实现                                      23

5.10.22.    Controller实现                                  24

5.11.    客户端实现                                             25

5.11.23.    角色编辑页面按钮事件处理               25

6.    角色修改页面呈现                                       26

6.12.    服务端实现                                             26

6.12.24.    Dao实现                                            26

6.12.25.    Mapper实现                                       27

6.12.26.    Service实现                                       28

6.12.27.    Controller实现                                   29

6.13.    客户端实现                                             29

6.13.28.    角色列表页面实现                              29

6.13.29.    角色编辑页面实现                              31

7.    角色修改操作实现                                        32

7.14.    服务端实现                                             32

7.14.30.    Dao实现                                            32

7.14.31.    Mapper实现                                      33

7.14.32.    Service实现                                      34

7.14.33.    Controller实现                                  35

7.15.    客户端实现                                            35

7.15.34.    角色编辑页面数据的更新                 35

8.    总结                                                           37

8.16.    重点和难点分析                                    37

8.17.    常见FAQ                                               38


1.角色列表页面呈现

1.服务端实现

  1. Controller实现

业务描述

创建角色控制层对象,并添加相关返回具体view.

页面实现

1.Controller类的创建

   a.包名 com.jt.sys.controller

    b.类名 SysRoleController

    c.映射 @RequestMapping("/role/")

2.Controller 类中方法定义,并返回role_list页面

    a.方法名 doRoleListUI

    b.参数列表()

    c.返回值 String

    d.映射 @RequestMapping("doRoleListUI")

代码实现:

@Controller
@RequestMapping("/role/")
public class SysRoleController {
@RequestMapping("doRoleListUI")
public String doRoleListUI(){
return "sys/role_list";
}
}

2.客户端实现

2.定义角色列表页面

3.在WEB-INF/pages/sys目录下定义role_list.html页面.

4.首页页面菜单事件处理

业务描述

6.1)在starter.html页面中注册角色管理的点击事件

7.2)在starter.html页面中定义事件处理函数,异步加载菜单列表页面

8.业务实现

9.在$(function(){})追加事件处理函数的调用loadUI()

关键代码实现:

$(function(){
doLoadUI(“#load-role-id","role/doRoleListUI.do"); 
});


14.角色分页div内容加载

页面描述

角色列表页面加载完成异步加载分页页面

业务实现

在role_list.html页面底部添加JS异步加载分页页面的实现

关键代码实现

$(function(){
$("#pageId").load("doPageUI.do");
})

效果:


2.角色列表数据呈现

4.服务端实现

4.Entity类实现

业务描述:

定义实体封装从数据库查询的数据

业务实现

构建与sys_roles表对应的实体类型

  1. 包名 com.jt.sys.entity

  2. 类名 SysConfig (实现序列化接口,并定义序列化id)

  3. 属性 与表(sys_configs)中字段有对应关系

  4. 方法 set/get

关键代码实现:

public class SysRole implements Serializable{
private static final long serialVersionUID = 3098457856539501697L;
private Integer id;
private String name;
private String note;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
....
}

5.Dao接口实现

业务描述:(核心-查询当前页显示的数据以及总记录数)

  1. 接收业务层参数数据

  2. 基于参数进行数据查询

  3. 将查询结果进行封装

  4. 将结果返回给业务层对象

业务实现:创建Dao接口并定义相关方法。

  1. Dao接口定义

       a.包名: com.jt.sys.dao

       b.名字: SysRoleDao

2.方法定义:负责基于条件查询当前页数据

       a.方法名:findPageObjects

       b.参数列表:(String name,Integer startIndex,Integer pageSize)

       c.返回值:List<SysConfig>

3.方法定义:负责基于条件查询总记录数

       a.方法名:getRowCount

       b.参数列表:(String name)

       c.返回值:int

思考:DAO中方法的参数在mapper中如何获取?

代码实现:

public interface SysRoleDao {
/**
* 分页查询角色信息
* @param startIndex 上一页的结束位置
* @param pageSize 每页要查询的记录数
* @return
*/
List<SysRole> findPageObjects(
@Param("name")Integer name,
@Param("startIndex")Integer startIndex,
@Param("pageSize")Integer pageSize);
/**
* 查询记录总数
* @return
*/
int getRowCount(@Param("name")String name);
}

6.Mapper实现

业务描述

  1. 基于Dao接口创建映射文件

  2. 基于Dao方法在映射文件中创建映射元素建映射元素

业务实现:

1.创建映射文件

   a.包名:mapper.sys

   b.文件名:SysRoleMapper.xml

   c.命名空间 com.jt.sys.dao.SysRoleDao

2.创建映射元素实现翻页查询操作

   a.元素名  select

   b.元素id  findPageObjects

   c.参数类型 (不写)

   d.结果类型 com.jt.sys.entity.SysRole

   e.SQL定义 select * from sys_roles where name like ? limit ?,?

3.创建映射元素实现查询统计操作

   a.元素名  select

  b.元素id  getRowCount

  c.参数类型 (不写)

  d.结果类型 int

  e.SQL定义 select count(*) from sys_roles where name like ?

代码实现:

<mapper namespace="com.jt.sys.dao.SysRoleDao">
<select id="findPageObjects"
resultType="com.jt.sys.entity.SysRole">
select *
from sys_roles
<include refid="queryWhereId"/>
order by createdTime desc
limit #{startIndex},#{pageSize}
</select>
<select id="getRowCount"
resultType="int">
select count(*)
from sys_roles
<include refid="queryWhereId"/>
</select>
<sql id="queryWhereId">
<where>
<if test="name!=null and name!=''">
name like concat("%",#{name},"%")
</if>
</where>
</sql>
</mapper>

7.Service层对象

业务描述:核心业务就是分页查询数据并对数据进行封装。

  1. 通过参数变量接收控制层数据

  2. 对数据进行合法验证

  3. 基于参数数据进行总记录数查询

  4. 基于参数数据进行当前页记录的查询

  5. 对数据进行封装

  6. ..........

业务实现:

1.Service接口定义

   a.包名:com.jt.sys.service

   b.接口名:SysRoleService

2.接口方法定义:

   a.方法名: findPageObjects

   b.参数列表:(String name,Integer pageCurrent)

   c.返回值:PageObject (通过此对象封装当前页数据,分页信息)

3.接口实现类定义

   a.包名: com.jt.sys.service.impl

   b.类名: SysRoleServiceImpl (实现SysRoleService接口)

关键代码实现:

业务接口定义

public interface SysRoleService {
/**
* 本方法中要分页查询角色信息,并查询角色总记录数据
* @param pageCurrent 当表要查询的当前页的页码值
* @return 封装当前实体数据以及分页信息
*/
PageObject<SysRole> findPageObjects(
String name,Integer pageCurrent);
}

业务实现类定义

@Service
public class SysRoleServiceImpl implements SysRoleService{
@Autowired
private SysRoleDao sysRoleDao;
@Override
public PageObject<SysRole> findPageObjects(
String name, Integer pageCurrent) {
//1.验证参数合法性
//1.1验证pageCurrent的合法性,
//不合法抛出IllegalArgumentException异常
if(pageCurrent==null||pageCurrent<1)
throw new IllegalArgumentException("当前页码不正确");
//2.基于条件查询总记录数
//2.1) 执行查询
int rowCount=sysRoleDao.getRowCount(name);
//2.2) 验证查询结果,假如结果为0不再执行如下操作
if(rowCount==0)return null;
//3.基于条件查询当前页记录(pageSize定义为2)
//3.1)定义pageSize
int pageSize=2;
//3.2)计算startIndex
int startIndex=(pageCurrent-1)*pageSize;
//3.3)执行当前数据的查询操作
List<SysRole> records=
sysRoleDao.findPageObjects(name, startIndex, pageSize);
//4.对分页信息以及当前页记录进行封装
//4.1)构建PageObject对象
PageObject<SysRole> pageObject=new PageObject<>();
//4.2)封装数据
pageObject.setPageCurrent(pageCurrent);
pageObject.setPageSize(pageSize);
pageObject.setRowCount(rowCount);
pageObject.setRecords(records);
//5.返回封装结果。
return pageObject;
}

8.Controller 实现

业务描述:核心业务是处理客户端请求

  1. 接收客户端请求中的数据

  2. 基于请求调用业务方法进行请求处理

  3. 对处理结果进行封装(JsonResult)

  4. 将结果转换为json格式的字符串

  5. 将字符串通过服务器输出到客户端。

业务实现:

控制层对象SysRoleController类中方法定义

  1. 方法名   doFindPageObjets

  2. 参数列表 (String name,Integer pageCurrent)

  3. 返回值   JsonResult

  4. 映射     doFindPageObjets

关键代码实现:

@Controller
@RequestMapping("/role/")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
@RequestMapping("doRoleListUI")
public String doRoleListUI(){
return "sys/role_list";
}
@RequestMapping("doFindPageObjects")
@ResponseBody
public JsonResult doFindPageObjects( String name,Integer pageCurrent){
PageObject<SysRole> pageObject=
sysRoleService.findPageObjects(name,pageCurrent);
return new JsonResult(pageObject);
} 
}

测试:部署测试

http://localhost:8080/CGB-JT-SYS-V1.01/role/doFindPageObjects.do?pageCurrent=1

效果:


5.客户端实现 

9.角色页面数据呈现

 业务描述:

  1. 页面加载完成发起异步请求加载配置信息

  2. 通过服务端返回的数据更新当前列表页面

业务实现

  1. 定义doGetObjects()函数,通过此函数执行异步加载操作。

  2. 分页页面加载完成以后调用doGetObjects().

代码实现:

定义页面加载完成以后的事件处理

WEB-INF/pages/sys/role_list.html 

$(function(){
$("#pageId").load("doPageUI.do",function(){
//异步加载服务端数据然后进行呈现
doGetObjects();
});
})

定义分页查询函数

function doGetObjects(){
//debugger;//断点调试
//1.定义url和参数
var url="role/doFindPageObjects.do"
//? 请问data函数的含义是什么?(从指定元素上获取绑定的数据)
//此数据会在何时进行绑定?(setPagination,doQueryObjects)
var pageCurrent=$("#pageId").data("pageCurrent");
//为什么要执行如下语句的判定,然后初始化pageCurrent的值为1
//pageCurrent参数在没有赋值的情况下,默认初始值应该为1.
if(!pageCurrent) pageCurrent=1;
var params={"pageCurrent":pageCurrent};
//2.发起异步请求
//请问如下ajax请求的回调函数参数名可以是任意吗?可以,必须符合标识符的规范
$.getJSON(url,params,function(result){
//JsonResult->PageObject->List<SysConfigs>+...
//请问result是一个字符串还是json格式的js对象?对象
doHandleResponseResult(result);
}
);
}

设置异步响应结果

function doHandleResponseResult(result){
if(result.state==1){//ok
//更新table中tbody内部的数据
doSetTableBodyRows(result.data.records);//将数据呈现在页面上
//更新页面page.html分页数据
doSetPagination(result.data);
}else{
alert(result.message);
} 
}

将异步响应结果呈现在table的tbody位置

/*设置表格内容*/
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=createdTds(records[i]);
//2.3 将tds追加到tr中
tr.append(tds);
//2.4 将tr追加到tbody中
tBody.append(tr);
}
}
//构建body中每行的td元素
function createdTds(row){
var tds="<td>"+row.name+"</td>"+
"<td>"+row.note+"</td>"+
"<td>"+new Date(row.createdTime).toLocaleString()+"</td>"+
"<td>"+new Date(row.modifiedTime).toLocaleString()+"</td>"+
"<td>"+row.createdUser+"</td>"+
"<td>"+row.modifiedUser+"</td>"+
"<td>"+
"<button type='button' class='btn btn-delete'>delete</button>"+
"&nbsp"+
"<button type='button' class='btn btn-update'>update</button>"+
"</td>";
return tds;
}

效果:


10.列表页面role_list.html信息查询实现

业务说明

  1. 列表查询按钮事件注册

  2. 列表查询按钮事件处理函数定义

  3. 列表查询参数获取以及传递

业务实现:

  1. 在$(function(){})回调函数中追加查询按钮的事件注册。

  2. 定义查询按钮的事件处理函数doQueryObjects

  3. 重用doGetObjects函数并添加查询参数name

关键代码实现:

查询按钮事件注册

$(".input-group-btn").on("click",".btn-search",doQueryObjects)

查询按钮事件处理函数定义

function doQueryObjects(){
//为什么要在此位置初始化pageCurrent的值为1?
//数据查询时页码的初始位置也应该是第一页
$("#pageId").data("pageCurrent",1);
//为什么要调用doGetObjects函数?
//重用js代码,简化jS代码编写。
doGetObjects();
}

在分页查询函数中追加name参数定义 

function doGetObjects(){
//debugger;//断点调试
//1.定义url和参数
var url="role/doFindPageObjects.do"
//? 请问data函数的含义是什么?(从指定元素上获取绑定的数据)
//此数据会在何时进行绑定?(setPagination,doQueryObjects)
var pageCurrent=$("#pageId").data("pageCurrent");
//为什么要执行如下语句的判定,然后初始化pageCurrent的值为1
//pageCurrent参数在没有赋值的情况下,默认初始值应该为1.
if(!pageCurrent) pageCurrent=1;
var params={"pageCurrent":pageCurrent};
//为什么此位置要获取查询参数的值?
//一种冗余的应用方法,目的时让此函数在查询时可以重用。
var name=$("#searchNameId").val();
//如下语句的含义是什么?动态在js对象中添加key/value,
if(name) params.name=name;//查询时需要
//2.发起异步请求
//请问如下ajax请求的回调函数参数名可以是任意吗?可以,必须符合标识符的规范
$.getJSON(url,params,function(result){
//请问result是一个字符串还是json格式的js对象?对象
doHandleResponseResult(result);
}
);
}

注意:

get请求乱码导致查询失败

数据库乱码

效果:


3.角色删除模块实现

6.服务端实现

11.DAO实现

业务描述:

  1. 接收业务层数据(id)

  2. 根据id删除角色自身信息

  3. 根据id删除角色与菜单的关系数据

  4. 根据id删除角色与用户的关系数据

业务实现

1.在SysRoleDao接口中添加删除对象的方法

    a.方法名 deleteObject;

    b.参数列表(Integer id)

    c.返回值int

2.在SysRoleMenuDao接口中添加删除对象的方法

    a.方法名 deleteObjectsByRoleId;

    b.参数列表(Integer roleId)

    c.返回值int

3.在SysUserRoleDao接口中添加删除对象的方法

   a.方法名 deleteObjectsByRoleId;

   b.参数列表(Integer roleId)

   c.返回值int

关键代码实现

SysRoleDao 接口中方法定义   

 int deleteObject(Integer id);

SysRoleMenuDao接口中方法定义   

int deleteObjectsByRoleId(Integer roleId);

SysUserRoleDao接口方法定义   

 int deleteObjectsByRoleId(Integer roleId);

12.Mapper实现

业务描述:

  1. 基于SysRoleDao中删除方法,定义SQL元素

  2. 基于SysRoleMenuDao中删除方法,定义SQL元素

  3. 基于SysUserRoleDao中删除方法,定义SQL元素

业务实现:

1.在SysRoleMapper中添加对应的删除元素

    a.元素名 delete

    b.元素id deleteObject

    c.参数类型(int)

    d.SQL定义 (delete from sys_roles where id=#{id})

2.在SysRoleMenuMapper中添加对应的删除元素

    e.元素名 delete

    f.元素id deleteObjectsByRoleId

    g.参数类型(int)

    h.SQL定义 (delete from sys_role_menus where role_id=#{roleId})

3.在SysUserRoleMapper中添加对应的删除元素

    i.元素名 delete

   j.元素id deleteObjectsByRoleId

   k.参数类型(int)

   l.Sql定义(delete from sys_user_roles where id=#{id})

关键代码实现

SysRoleMapper文件中方法定义

<delete id="deleteObject"
parameterType="int">
delete
from sys_roles
where id=#{id}       
</delete>

SysRoleMenuMapper中方法定义 

<delete id="deleteObjectsByRoleId"
parameterType="int">
delete
from sys_role_menus
where role_id=#{roleId}       
</delete>

SysUserRoleMapper 接口中方法定义

<delete id="deleteObjectsByRoleId"
parameterType="int">
delete
from sys_user_roles
where role_id=#{roleId}       
</delete>

13.Service实现

业务描述

  1. 接收控制层数据(id),并对数据进行合法性验证.

  2. 调用dao方法,执行删除操作

  3. 根据结果验证,反馈相关信息

业务实现(在SysRoleService接口中定义方法并在实现类中进行实现.)

  1. 方法名 deleteObject

  2. 参数列表 (Integer id)

  3. 返回值 int

代码实现:

@Override
public int deleteObject(Integer id) {
//1.验证参数的合法性
if(id==null||id<1)
throw new ServiceException("id的值不正确,id="+id);
//2.执行dao操作
int rows=sysRoleDao.deleteObject(id);
if(rows==0)
throw new ServiceException("数据可能已经不存在");
sysRoleMenuDao.deleteObjectsByRoleId(id);
sysUserRoleDao.deleteObjectsByRoleId(id);
//3.返回结果
return rows;
}

 14.Controller实现

业务描述

  1. 接收客户端请求数据id

  2. 调用业务层方法删除数据

  3. 返回响应结果.

业务实现(在SysRoleController中定义删除方法)

  1. 方法名 doDeleteObject

  2. 参数列表 Integer id

  3. 返回值 JsonResult

代码实现:

@RequestMapping("doDeleteObject")
@ResponseBody
public JsonResult doDeleteObject(Integer id){
sysRoleService.deleteObject(id);
return new JsonResult("delete Ok");
}

 7.客户端实现

15.角色列表页面删除操作实现

业务描述

点击每行的delete按钮时执行删除角色的操作

业务实现

  1. 页面加载完成以后在删除按钮上注册点击事件

$(function(){
$("#pageId").load("doPageUI.do",function(){
//异步加载服务端数据然后进行呈现
doGetObjects();
});
//查询按钮事件注册
$(".input-group-btn").on("click",".btn-search",doQueryObjects);
//删除按钮上注册点击事件
$("#tbodyId").on("click",".btn-delete",doDeleteObject);
});
/*
*将异步响应结果呈现在table的tbody位置
*/
function doSetTableBodyRows(records){
console.log("records:"+records);
//1.获取tbody对象,并清空对象
var tBody = $("#tbodyId");
tBody.empty();

//2.迭代records记录,并将其内容追加到tbody
for(var i in records){
//2.1 构建tr对象
var tr = $("<tr></tr>");
tr.data("id",records[i].id);

//2.2 构建tds对象
var tds = createdTds(records[i]);
//2.3 将tds追加到tr中
tr.append(tds);
//2.4 将tr追加到tbody中
tBody.append(tr);
}
}
  1. 事件处理函数doDeleteObject定义

关键代码实现:

function doDeleteObject(){
//1.获取选中的值(分页显示记录时在tr上要绑定id的值)
var id=$(this).parents("tr").data("id");
//2.构建参数对象
var params={“id”:id};
//3.异步请求执行删除
var url="role/doDeleteObject.do";
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doGetObjects();
}else{
alert(result.message);
}
})
}

4.角色添加页面呈现

8.服务端实现

16.角色Controller对象的实现

业务描述

基于客户端的请求返回一个页面(role_edit.html)

业务实现

  1. 方法名 doRoleEditUI

  2. 参数列表 ()

  3. 返回值 String

  4. 映射 @RequestMapping("doRoleEditUI")

    5.类名:SysRoleController.java

代码实现

@RequestMapping("doRoleEditUI")
public String doRoleEditUI(){
return "sys/role_edit";
}

9.客户现端实现

17.角色列表添加按钮事件处理

业务描述

点击角色列表页面中的添加按钮时呈现角色添加页面.

业务实现:

  1. 添加按钮注册点击事件

  2. 定义事件处理函数(异步加载角色编辑页面)

代码实现:

文件名:role_list.html

$(".input-group-btn").on("click",".btn-add",doLoadEditUI);
//异步加载编辑页面
function doLoadEditUI(){
var title;
//hasClass函数用于判定对象中是否包含某个样式
if($(this).hasClass("btn-add")){
title="角色添加";
}else{
title="角色修改";
}
loadPageUI(url);
}
function loadPageUI(title){
$("#mainContentId")
.load("role/doRoleEditUI.do",function(){
$(".box-title").html(title);
});
}

效果:


 18.角色添加页面菜单信息呈现role_edit.html

业务描述

页面加载完成,加载菜单信息,并通过菜单数据初始化菜单树(ZTree)

业务实现

定义doLoadSysMenus方法(负责加载ztree信息并初始化树结构)

关键代码实现

定义zTree配置

var zTree;
var setting = {
data : {
simpleData : {
enable : true,
idKey : "id",  //节点数据中保存唯一标识的属性名称
pIdKey : "parentId",  //节点数据中保存其父节点唯一标识的属性名称
rootPId : null  //根节点id
}
},
check:{
enable:true,
nocheckInherit:true
}
}

页面加载完成通过如下函数异步加载菜单信息

function doLoadSysMenus(){
var url="menu/doFindZtreeMenuNodes.do"
$.getJSON(url,function(result){
if(result.state==1){
zTree=$.fn.zTree.init(
$("#menuTree"),setting,result.data);
}else{
alert(result.message);
}
});
}

效果 :


5.角色添加操作实现

10.服务端实现

19.Dao实现

业务描述

5.将角色自身信息保存到数据库

6.将角色与菜单的关系数据保存到数据库

业务实现

1.在SysRoleDao接口中添加一个方法,用于将角色信息写入到数据库

   a.方法名 insertObject

   b.参数列表(SysRole entity)

   c.返回值 int

2.在SysRoleMenuDao中添加插入角色菜单关系数据的方法

   a.方法名 insertObject

   b.参数列表(Integer roleId, Integer[] menuIds)

   c.返回值 int

代码实现

SysRoleDao接口中方法定义

int insertObject(SysRole entity);

SysRoleMenuDao接口中方法定义(不存在则创建)

int insertObject(
@Param("roleId")Integer roleId,
@Param("menuIds")Integer[] menuIds);

 20.Mapper实现

业务描述

  1. 基于SysRoleDao接口中insertObject方法定义相关元素

  2. 基于SysRoleMenuDao接口中insertObject方法定义相关元素

业务实现

1.在SysRoleMapper中定义插入数据的元素

  a.元素名 insert

  b.元素id insertObject

  c.参数类型 com.jt.sys.entity.SysRole

  d.SQL定义 (insert into sys_roles (....) values(....))

2.在SysRoleMenuMapper中定义插入数据的元素

  a.元素名 insert

  b.元素id insertObject

  c.参数类型 (无)

  d.SQL定义 (insert into sys_role_menus (....) values(....))

关键代码实现:

SysRoleMapper中元素定义

<insert id="insertObject"
parameterType="com.jt.sys.entity.SysRole"
useGeneratedKeys="true"
keyProperty="id">
insert into sys_roles
(id,name,note,createdTime,modifiedTime,
createdUser,modifiedUser)
values
(null,#{name},#{note},now(),now(),
#{createdUser},#{modifiedUser})
</insert>

SysRoleMenuMapper中元素定义

<insert id="insertObject">
insert into sys_role_menus
(role_id,menu_id)
values
<foreach collection="menuIds"
separator=","
item="item">
(#{roleId},#{item})
</foreach>
</insert>

 21.Service实现

业务描述

  1. 通过参数变量接收控制层数据

  2. 对参数数据进行合法性验证.

  3. 保存角色自身信息数据到数据库

  4. 保存角色与菜单的关系数据到数据库

  5. 返回业务实现结果

业务实现

在SysRoleService接口中定义插入数据的方法

   a.方法名 saveObject

   b.参数列表 SysRole entity,Integer[] menuIds

   c.返回值 int

关键代码实现

@Override
public int saveObject(SysRole entity,Integer[] menuIds) {
//1.合法性验证
if(entity==null)
throw new ServiceException("保存数据不能为空");
if(StringUtils.isEmpty(entity.getName()))
throw new ServiceException("角色名不能为空");
if(StringUtils.isEmpty(menuIds))
throw new ServiceException("必须为角色赋予权限");
//2.保存数据
int rows=sysRoleDao.insertObject(entity);
sysRoleMenuDao.insertObject(
entity.getId(),menuIds);
//3.返回结果
return rows;
}

 22.Controller实现

业务描述

  1. 接收客户端请求数据并对数据进行封装

  2. 调用业务层方法将数据写入数据库

  3. 对控制层数据进行封装(JsonResult),并返回

业务实现

在SysRoleController中定义请求处理方法

    a.方法名 doSaveObject

    b.参数列表 (SysRole entity,Integer[] menuIds)

    c.返回值 JsonResult

    d.映射 @RequestMapping("doSaveObject")

关键代码实现:

@RequestMapping("doSaveObject")
@ResponseBody
public JsonResult doSaveObject(
SysRole entity,Integer[] menuIds){
sysRoleService.saveObject(entity,menuIds);
return new JsonResult("save ok");   
}

效果:


 11.客户端实现

23.角色编辑页面按钮事件处理role_edit.html

业务描述

  1. 点击cancel按钮时,退出编辑页面进入列表页面.

  2. 点击save按钮时,将数据异步提交到服务端进行保存,保存ok时返回列表页面.

业务实现(cancel按钮事件处理)

function doCancel(){
$("#mainContentId")
.load("role/doRoleListUI.do",function(){
//移除绑定的数据(修改时会用)
$("#mainContentId").removeData();
});
}

业务实现(save按钮事件处理)

function doSaveOrUpdate(){
//1.获取表单数据
var params=getEditFormData(); 
//2.异步提交表单数据
var insertUrl="role/doSaveObject.do";
$.post(insertUrl,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
});
}
//获取表单数据
function getEditFormData(){
var params={
name:$("#nameId").val(),
note:$("#noteId").val()
}
//获取选中的node节点
var menuIds=[];
var checkedNodes=
zTree.getCheckedNodes(true);
for(var i in checkedNodes){
menuIds.push(checkedNodes[i].id)
}
params.menuIds=menuIds.toString();//(1,2,3,4,5)
return params;
}

 6.角色修改页面呈现

12.服务端实现

24.Dao实现

业务描述

  1. 基于角色id查询角色信息

  2. 基于角色id查询菜单信息

业务实现

1.在SysRoleDao中定义基于基于角色id查询角色信息的方法

   a.方法名 findObjectById

   b.参数列表 Integer id

   c.返回值 SysRole

2.在SysRoleMenuDao中定义基于角色查询菜单id的方法

   a.方法名 findMenuIdsByRoleId

   b.参数列表 Integer roleId

  c.返回值 List<Integer>

关键代码实现

SysRoleDao中方法定义

SysRole findObjectById(Integer id);

SysRoleMenuDao中方法定义

List<Integer> findMenuIdsByRoleId(Integer roleId);

 25.Mapper实现

业务描述

  1. 基于SysRoleDao接口中方法的定义编写基于角色id执行查询角色的元素

  2. 基于SysRoleMenuDao接口中方法的定义编写基于角色id查询菜单id的方法

业务实现

1.SysRoleMapper中元素定义

   a.元素名  select

   b.元素id  findObjectById

   c.参数类型 (int)

   d.结果类型 com.jt.sys.entity.SysRole

   e.SQL定义 select * from sys_roles where id=?

2.SysRoleMenuMapper中元素定义

   a.元素名  select

   b.元素id  findMenuIdsByRoleId

   c.参数类型 (int)

   d.结果类型 int

   e.SQL定义 select menu_id from sys_role_menus where role_Id=?

关键代码实现

SysRoleMapper中元素定义   

<select id="findObjectById"
resultType="com.jt.sys.entity.SysRole">
select *
from sys_roles
where id=#{id}
</select>

SysRoleMenuMapper中元素定义

<select id="findMenuIdsByRoleId"
resultType="int">
select menu_id
from sys_role_menus
where role_id=#{roleId}
</select>

 26.Service实现

业务描述:基于角色id查询角色及关联的菜单信息

  1. 通过参数变量接收控制层数据

  2. 对数据进行合法验证

  3. 基于参数数据查询角色信息

  4. 基于参数数据查询菜单信息

  5. 对数据进行封装并返回

业务实现:

在SysRoleService接口及实现类中定义基于id查询的方法

关键代码实现

接口中方法定义

Map<String,Object> findObjectById(Integer id) ;

实现类中方法的实现 

@Override
public Map<String,Object> findObjectById(Integer id) {
//1.合法性验证
if(id==null||id<=0)
throw new ServiceException("id的值不合法");
//2.执行查询
SysRole role=sysRoleDao.findObjectById(id);
List<Integer> menuIds=sysRoleMenuDao.findMenuIdsByRoleId(id);
//3.验证结果并返回
if(role==null)
throw new ServiceException("此记录已经不存在");
Map<String,Object> map=new HashMap<String, Object>();
map.put("role", role);
map.put("menuIds", menuIds);
return map;
}

 27.Controller实现

业务描述:核心业务是处理客户端请求

  1. 接收客户端请求中的数据(id)

  2. 基于请求调用业务方法进行请求处理

  3. 对处理结果进行封装(JsonResult)

  4. 将结果转换为json格式的字符串

  5. 将字符串通过服务器输出到客户端。

业务实现:

控制层对象SysRoleController类中方法定义

  1. 方法名   doFindObjectById

  2. 参数列表 (Integer id)

  3. 返回值   JsonResult

  4. 映射     doFindObjectById

关键代码实现

@RequestMapping("doFindObjectById")
@ResponseBody
public JsonResult doFindObjectById(Integer id){
Map<String,Object> map=
sysRoleService.findObjectById(id);
return new JsonResult(map);
}

 13.客户端实现

28.角色列表页面实现

业务描述

  1. 点击列表页面修改按钮时基于id查询异步角色信息

  2. 通过角色信息初始化编辑页面数据

业务实现

  1. 列表页面修改按钮事件注册

  2. 列表页面修改按钮事件处理函数定义(与添加操作公用一个方法并进行适当修改)

关键代码实现

$("tbody").on("click",".btn-update",doLoadEditUI);
/加载编辑页面
function doLoadEditUI(){
//定义页面标题(内容可能是添加角色也可能是修改角色)
var title;
//判定要执行的操作(是添加还是修改)
if($(this).hasClass("btn-add")){
title="添加角色";
loadPageUI(title);
}else{
title="修改角色";
//获取当前行的id值
var id=$(this).parents("tr").data("id");
//根据id查找记录,判定记录是否存在
var url="role/doFindObjectById.do";
var data={"id":id};
$.getJSON(url,data,function(result){
if(result.state==1){
//此位置除了要分析正确还要考虑对象不存在的情况
$("#mainContentId").data("data",result.data)
loadPageUI(title);
}else{
alert(result.message);
}
});
}
}  
function loadPageUI(title){
$("#mainContentId")
.load("role/doRoleEditUI.do",function(){
$(".box-title").html(title);
});
}

效果:


29.角色编辑页面实现

业务描述

1)菜单树初始化完成,获取绑定数据

2)通过绑定数据初始化表单

代码实现:

菜单树初始化完成,获取绑定数据,初始化表单

关键代码

function doLoadSysMenus(){
var url="menu/doFindZTreeNodes.do"
$.getJSON(url,function(result){
if(result.state==1){
ztree=$.fn.zTree.init(
//先有菜单树后再选择树数据
$("#menuTree"),setting,result.data);
var data=$("#mainContentId").data("data");
if(data){
doInitEditFormData(data);
}
}else{
alert(result.message);
}
})
}

初始化表单数据

function doInitEditFormData(data){
$("#nameId").val(data.role.name);
$("#noteId").val(data.role.note);
//展开所有节点
zTree.expandAll(true);
//勾选角色所拥有的菜单
var menuIds = data.menuIds;
for(var i=0; i<menuIds.length; i++) {
//获取key为id值为menuIds[i]的节点
var node = zTree.getNodeByParam("id",menuIds[i]);
//选中当前节点
zTree.checkNode(node,true,false);
}
}

效果:


 7.角色修改操作实现

14.服务端实现

30.Dao实现

业务描述

修改角色自身信息

修改角色与菜单的关系数据

业务实现:

1)在SysRoleDao方法中添加角色修改的方法

    a.方法名 updateObject

    b.参数列表 (SysRole entity)

    c.返回值 int

2)在SysRoleMenuDao中添加根据角色id删除关系数据的方法

   a.方法名 deleteObjectsByRoleId (假如已经存在则无需再次定义)

   b.参数(Integer roleId)

   c.返回值 int

关键代码实现:

SysRoleDao中方法定义

int updateObject(SysRole entity);

SysRoleMenuDao中方法定义

int deleteObjectsByRoleId(Integer roleId);

 31.Mapper实现

业务描述

基于SysRoleDao中updateObject方法的定义编写对应sql元素

基于SysRoleMenuDao中deleteObjectsByRoleId方法的定义编写对应sql元素

业务实现

1)在SysRoleMapper文件中添加update元素,定义角色信息修改的sql语句

   a.元素名 update

   b.元素id updateObject

   c.参数类型 com.jt.sys.entity.SysRole

   d.Sql定义(update sys_roles set.... where ....)

2)在SysRoleMenuMapper中添加删除数据的元素

   a.元素名  delete

   b.元素id  deleteObjectsByRoleId

   c.参数类型  int

   d.Sql 定义 delete from sys_role_menus where role_id=#{roleId};

代码实现:

SysRoleMapper元素定义

<update id="updateObject" parameterType="com.jt.sys.entity.SysRole">
update sys_roles
<set>
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="note!=null and note!=''">
note=#{note},
</if>
<if test="modifiedUser!=null and modifiedUser!=''">
modifiedUser=#{modifiedUser},
</if>
modifiedTime=now()
</set>
where id=#{id}
</update>

SysRoleMenuMapper 元素定义(假如已有则不用再写)

<delete id="deleteObjectsByRoleId"
parameterType="int">
delete from sys_role_menus
where role_id=#{roleId}
</delete>

32.Service实现

业务描述

  1. 接收控制层数据,对数据进行合法验证.

  2. 调用dao层方法更新数据

  3. 验证结果,并返回.

业务实现(在SysRoleService接口及实现类中定义方法及实现.)

  1. 方法名 updateObject

  2. 参数列表 SysRole entity,String menuIds

  3. 返回值 int

代码实现:

@Override
public int updateObject(SysRole entity,String menuIds) {
//1.合法性验证
if(entity==null)
throw new ServiceException("更新的对象不能为空");
if(entity.getId()==null)
throw new ServiceException("id的值不能为空");
if(StringUtils.isEmpty(entity.getName()))
throw new ServiceException("角色名不能为空");
if(StringUtils.isEmpty(menuIds))
throw new ServiceException("必须为角色指定一个权限");
//2.更新数据
int rows=sysRoleDao.updateObject(entity);
if(rows==0)
throw new ServiceException("对象可能已经不存在");
sysRoleMenuDao.deleteObjectsByRoleId(entity.getId());
sysRoleMenuDao.insertObject(entity.getId(),menuIds.split(","));
//3.返回结果
return rows;
}

33.Controller实现

业务描述

  1. 接收客户端请求数据(角色信息,以及菜单id)

  2. 调用业务层对象处理业务

  3. 对结果进行封装

  4. 将响应结果转换为json输出

业务实现(在SysRoleController中定义修改方法)

  1. 方法名 doUpdateObject

  2. 参数列表(SysRole entity)

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doUpdateObject")

代码实现:

@RequestMapping("doUpdateObject")
@ResponseBody
public JsonResult doUpdateObject(SysRole entity,
String menuIds){
sysRoleService.updateObject(entity,menuIds);
return new JsonResult("update ok");

 15.客户端实现

34.角色编辑页面数据的更新

业务描述:用户点击保存按钮时异步提交表单数据

  1. 获取表单数据(相对于添加操作应该再添加一个id)

  2. 异步提交(post)表单数据(修改和添加不是同一个url)

  3. 关闭页面,显示列表页面

业务实现:

  1. 修改doSaveOrUpdate方法与添加操作共用此方法

  2. 与添加操作共用一个获取表单的方法

关键代码实现

修改doSaveOrUpdate函数(与保存操作共享一个函数)

//点击保存按钮时执行此方法
function doSaveOrUpdate(){//insert/update
//获取表单数据
var params=getEditFormData();
//假如当前页面.container-fluid对象上绑定着值说明是修改
var data=$("#mainContentId").data("data");
if(data){
params.id=data.role.id;//修改时表单数据中需要添加id
}
//根据当前页面上是否绑定着值来定义url
var insertUrl="role/doSaveObject.do";
var updateUrl="role/doUpdateObject.do";
var url=data?updateUrl:insertUrl;
//异步提交数据
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
})
}

获取表单数据

function getEditFormData(){
var params={
name:$("#nameId").val(),
note:$("#noteId").val()
}
//获取选中的node节点
var menuIds=[];
var checkedNodes=
ztree.getCheckedNodes(true);
for(var i in checkedNodes){
menuIds.push(checkedNodes[i].id)
}
params.menuIds=menuIds.toString();
return params;
}

效果:

去掉部分数据选项

再次点击更新:

成功:

代码中的bug:

解决方案:

没有数据时也修改一些页码页数据

课后作业:

做完组织管理


8.总结

16重点和难点分析

1.角色业务处理流程(持久层,业务层,控制层,表示层)

2.角色业务数据封装(SysRole,PageObject,JsonResult)

3.角色业务异常的统一处理(统一异常处理类的定义)

4.角色日期数据的格式转换(3种方案)

5.角色页面中JS实现(业务,方法的应用,编写,调试)

6.角色查询业务实现流程(持久层,mapper,业务层,控制层,表示层)

7.角色删除业务的具体实现流程(..........)

8.角色列表页面如何获取选中的checkbox对应的id值

9.角色列表页面如何实现全选和取消全选操作.(扩展实现)

思考JS函数:(doc.tedu)

  1. ajax({url:url,type:post,dataType:json,data:params,success:function(){}})

  2. load(url,[params],[callback])

  3. getJSON(url,params,function(result){})

  4. data(key,[value])

  5. prop(propertyName,[propertyValue])

  6. on("click",".pre,.next",doJumpToPage)

  7. post(url,[params],[callback])

  8. change()

  9. each(function(){})

  10. ..............


17.常见FAQ

  1. 为什么要定义实体类?(实现和表之间的映射,更好的封装数据)

  2. 类为什么要实现序列化接口?(已知)

  3. 类实现序列化接口以后为什么要添加序列化版本id?(已知道)

  4. 如何实现对象的序列化时的粒度控制?(哪些属性要序列化,哪些属性不需要序列化)(已知)

  5. 类对象序列化时内容如何加密?(已知)

  6. 角色业务层查询到的数据是如何封装的?(PageObject)

  7. 角色控制层数据是如何封装的?(JsonResult)

  8. 角色控制层异常是如何处理的?(全局统一处理)

  9. 角色控制层方法返回的对象是如何转换为JSON串的?(jackson)

  10. 角色控制层日期类型的数据是如何处理的?

  11. 实体对象SysRole的set/get方法何时有调用?

  12. 映射文件中的sql元素用于做什么?(提取sql共性)

  13. 映射文件中的动态sql元素常用的有哪些?(foreach,where,if,set,...)


 1

                                                                        齐雷


day05:京淘项目-权限管理子系统-用户模块

1.    用户模块列表呈现                                        3

1.1.    服务端呈现                                                3

1.1.1.    Controller实现                                        3

1.2.    客户端呈现                                                4

1.2.2.    首页Starter页面异步加载                        4

1.2.3.    用户列表页面加载分页页面                     4

2.    用户模块列表数据呈现                                   4

2.3.    服务端实现                                                  4

2.3.4.    Entity类实现                                             4

2.3.5.    Dao接口实现                                           9

2.3.6.    Mapper文件实现                                    10

2.3.7.    Service接口及实现类                             12

2.3.8.    Controller类实现                                    13

2.4.    客户端实现                                               14

2.4.9.    用户列表页面异步请求加载数据           14

2.4.10.    用户列表页面数据异步查询实现         16

3.    用户禁用启用操作实现                                 17

3.5.    服务端实现                                                17

3.5.11.    Dao接口实现                                        17

3.5.12.    Mapper文件实现                                   17

3.5.13.    Service接口实现                                  18

3.5.14.    Controller类实现                                  19

3.6.    客户端实现                                               19

3.6.15.    用户列表页面禁用按钮事件处理          20

4.    用户添加页面呈现                                       21

4.7.    服务端实现                                              21

4.7.16.    用户Controller实现                            21

4.8.    客户端实现                                            21

4.8.17.    用户列表页面添加按钮事件处理         21

5.    用户添加页面菜单数据呈现                         22

5.9.    服务端实现                                             22

5.9.18.    Vo对象定义                                       22

5.9.19.    Dao接口实现                                    23

5.9.20.    Mapper文件实现                               24

5.9.21.    Service接口实现                              24

5.9.22.    Controller实现                                 25

5.10.    客户端实现                                        25

5.10.23.    用户编辑页面实现                          25

6.    用户数据保存实现                                     26

6.11.    服务端实现                                          26

6.11.24.    Dao 接口实现                                26

6.11.25.    Mapper文件实现                             27

6.11.26.    用户Service实现                            28

6.11.27.    用户Controller实现                         29

6.12.    客户端实现                                          30

6.12.28.    用户编辑页面事件处理                       30

7.    用户页面修改呈现                                       32

7.13.    服务端实现                                            32

7.13.29.    用户DAO实现                                  32

7.13.30.    用户Mapper实现                              32

7.13.31.    用户角色DAO实现                             33

7.13.32.    用户角色Mapper实现                        33

7.13.33.    用户Service实现                               34

7.13.34.    用户Controller实现                           35

7.14.    客户端实现                                            35

7.14.35.    用户列表页面业务实现                       35

7.14.36.    用户编辑页面数据初始化                     37

8.    用户编辑页面数据更新                                    38

8.15.    服务端实现                                                 38

8.15.37.    用户Dao实现                                         38

8.15.38.    用户Mapper实现                                   39

8.15.39.    用户角色Dao实现                                  40

8.15.40.    用户角色Mapper实现                           40

8.15.41.    用户Service实现                                   41

8.15.42.    用户Controller实现                              42

8.16.    客户端实现                                               42

8.16.43.    用户编辑页面事件处理.                        43

9.    总结                                                               44

9.17.    重点和难点分析                                       44

9.18.    常见FAQ                                                 44


1.用户模块列表呈现(到这里了) 

1.服务端呈现

1.Controller实现

业务描述

创建一个Controller并定义一个方法,此方法直接返回用户列表页面.

类的定义

  1. 包名:com.jt.sys.controller

  2. 类名:SysUserController

  3. 映射:@RequestMapping("/user/")

类中方法定义

  1. 方法名 listUI

  2. 参数列表()

  3. 返回值 String

  4. 映射 @RequestMapping("doUserListUI")

代码实现:

@Controller
@RequestMapping("/user/")
public class SysUserController {
@RequestMapping("doUserListUI")
public String doUserListUI(){
return "sys/user_list";
}
}

 2.客户端呈现

2.首页Starter页面异步加载

业务描述

点击用户管理时,启动异步加载操作,将服务端返回页面,

添加到starter页面的div中.

代码实现:

<script type="text/javascript">
$(function(){
…
doLoadUI("load-user-id","user/doUserLsistUI.do");
});
</script>

 3.用户列表页面加载分页页面

业务描述

用户列表页面加载完成,异步加载分页页面.

代码实现

$(document).ready(function(){
$("#pageId").load("doPageUI.do");
});

 2.用户模块列表数据呈现

3.服务端实现

4.Entity类实现

业务描述

  1. 定义实体对象实现与sys_users表的映射

  2. 定义值对象封装查询时获取的用户与部门相关信息

业务实现

1.构建实体对象,属性与表中字段有映射关系

2.提供set/get,无参构造方法,重写toString方法

3.实现序列化接口,添加序列化版本id

1.实体对象类的定义

   a.包名 com.jt.sys.entity

   b.类名 SysUser (实现序列化接口并添加序列化ID)

   c.属性 与sys_users表中字段对应

   d.方法 提供set/get方法

代码实现:

实体类定义(后续添加和修改时可以用于在控制层接收客户端数据)

//定义值对象封装查询时获取的用户与部门相关信息public class SysUser implements Serializable{
private static final long serialVersionUID = 177030063138338860L;
private Integer id;
private String username;
private String password;
private String salt;
private String email;
private String mobile;
private Integer valid=1;
private Integer deptId;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Integer getValid() {
return valid;
}
public void setValid(Integer valid) {
this.valid = valid;
}
@JsonSerialize(using=DateJsonSerializer.class)
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
@JsonSerialize(using=DateJsonSerializer.class)
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
}

值对象定义(封装用户信息与其关联的部门信息的查询结果)

public class SysUserDeptResult implements Serializable{
private static final long serialVersionUID = 5477389876913514595L;
private Integer id;
private String username;
private String password;//md5
private String salt;
private String email;
private String mobile;
private Byte valid=1;
private SysDept sysDept;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
public Byte getValid() {
return valid;
}
public void setValid(Byte valid) {
this.valid = valid;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public SysDept getSysDept() {
return sysDept;
}
public void setSysDept(SysDept sysDept) {
this.sysDept = sysDept;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
}

 5.Dao接口实现

业务描述

创建用户持久层接口并定义查询方法获取表中用户信息.

接口定义

  1. 包: com.jt.sys.dao

  2. 接口名:SysUserDao

接口中方法定义(依据查询当前页数据)

  1. 方法名 findPageObjects

  2. 参数列表(String username,Integer startIndex,Integer pageSize)

  3. 返回值List<SysUser>

接口中方法定义(依据条件查询总记录数)

  1. 方法名 getRowCount

  2. 参数列表(String username)

  3. 返回值int

代码实现:

public interface SysUserDao {
List<SysUserDeptResult> findPageObjects(
@Param("username") String username,
@Param("startIndex")Integer startIndex,
@Param("pageSize")Integer pageSize);
int getRowCount(@Param("username") String username);
}

 6.Mapper文件实现

业务描述

1)依据SysUserDao接口及方法定义SysUserMapper文件和mapper元素。

业务实现:

1.创建映射文件

    a.包名:mapper.sys

    b.文件名:SysUserMapper.xml

    c.命名空间 com.jt.sys.dao.SysUserDao

2.在SysDeptMapper文件中定义基于id查询部门信息的元素

    a.元素名  select

    b.元素id  findDeptById

    c.参数类型 (不写)

    d.结果映射 com.jt.sys.entity.SysDept

    e.SQL定义 select * from sys_depts where id=?

3.在SysUserMapper文件中创建映射元素实现分页查询操作

    f.元素名  select

    g.元素id  findPageObjects

    h.参数类型 (不写)

     i.结果映射 sysUserMap

     j.SQL定义 select * from sys_users where username like ? limit …

4.在SysUserMapper文件中创建映射元素实现查询统计操作

     a.元素名  select

     b.元素id  getRowCount

     c.参数类型 (不写)

     d.结果类型 int

     e.SQL定义 select count(*) from sys_users where username like ?

代码实现

SysDeptMapper文件中元素定义

<select id="findById"
resultType="com.jt.sys.entity.SysDept">
select *
from sys_depts
where id=#{id}
</select>

SysUserMapper文件中元素定义

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis//DTD Mapper 3.0//EN"
"http://mybatis/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jt.sys.dao.SysUserDao">
<resultMap id="sysUserMap"
type="com.jtmon.vo.SysUserDeptResult">
<!-- 关联查询 -->
<association property="sysDept"
column="deptId"
select="com.jt.sys.dao.SysDeptDao.findById">
</association>
</resultMap>
<select id="findPageObjects"
resultMap="sysUserMap">
select * from sys_users
<include refid="queryWhereId"/>
limit #{startIndex},#{pageSize}
</select>
<select id="getRowCount"
resultType="int">
select count(*) from sys_users
<include refid="queryWhereId"/>
</select>
<sql id="queryWhereId">
<where>
<if test="username!=null and username!=''">
username like concat("%",#{username},"%")
</if>
</where>
</sql>
</mapper>

7.Service接口及实现类

业务描述

  1. 接收控制层数据,并对数据进行合法验证.

  2. 调用dao层方法,依据条件获取当前数据以及总记录数

  3. 封装数据并返回.

业务实现

业务层接口定义

  1. 包名 com.jt.sys.service

  2. 接口名 SysUserService

接口方法定义

  1. 方法名 findPageObjects

  2. 参数列表(String username,Integer pageCurrent)

  3. 返回值 PageObject

接口实现类定义

  1. 包名 com.jt.sys.service.impl

  2. 类名 SysUserServiceImpl

代码实现

接口定义

public interface SysUserService {
PageObject<SysUserDeptResult> findPageObjects(
String username,
Integer pageCurrent);
}

接口实现类及方法定义

@Service
public class SysUserServiceImpl implements SysUserService {
@Resource
private SysUserDao sysUserDao;
@Autowired
private SysUserRoleDao sysUserRoleDao;
@Override
public PageObject<SysUserDeptResult> findPageObjects(String username,
Integer pageCurrent) {
//1.数据合法性验证
if(pageCurrent==null||pageCurrent<=0)
throw new ServiceException("参数不合法");
//2.计算startIndex的值
int pageSize=3;
int startIndex=(pageCurrent-1)*pageSize;
//3.依据条件获取当前页数据
List<SysUserDeptResult> records=
sysUserDao.findPageObjects(
username, startIndex, pageSize);
//4.依据条件获取总记录数
int rowCount=sysUserDao.getRowCount(username);
//5.封装数据
PageObject<SysUserDeptResult> pageObject=new PageObject<>();
pageObject.setPageCurrent(pageCurrent);
pageObject.setRowCount(rowCount);
pageObject.setPageSize(pageSize);
pageObject.setRecords(records);
return pageObject;
}
}

 8.Controller类实现

业务描述

  1. 接收客户端请求数据

  2. 调用业务方法处理请求

  3. 封装响应数据并返回

业务实现(控制层SysUserController类中方法定义)

  1. 方法名 doFindPageObjects

  2. 参数列表(String username,Integer pageCurrent)

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doFindPageObjects")

代码实现:

在SysUserController类添加方法。

@RequestMapping("doFindPageObjects")
@ResponseBody
public JsonResult doFindPageObjects(
String username,Integer pageCurrent){
PageObject<SysUser> pageObject=
sysUserService.findPageObjects(
username, pageCurrent);
return new JsonResult(pageObject);
}

效果:


 4.客户端实现

9.用户列表页面异步请求加载数据

业务描述

  1. 分页div加载完成,异步加载数据.

  2. 将数据更新到页面.

业务实现(异步请求方法定义)

  1. 方法名 doGetObjects

  2. 参数列表()

$(document).ready(function(){
$("#pageId").load("doPageUI.do",doGetObjects);
});
function doGetObjects(){
//url
var url="user/doFindPageObjects.do";
//获取参数数据
var pageCurrent=$("#pageId")
.data("pageCurrent");
if(!pageCurrent)pageCurrent=1;
var  params={"pageCurrent":pageCurrent}
//获取查询参数username的值(此步骤在查询时使用)
var username=$("#searchNameId").val();
if(username)params.username=username;
//发送异步请求
$.getJSON(url,params,function(result){
if(result.state==1){
setTableBodyRows(result.data.records);
setPagination(result.data);
}else{
alert(result.message);
}
});
}
function setTableBodyRows(records){
//1.获取body对象,并清空内容
var tBody=$("#tbodyId");
tBody.empty();
//2.迭代records元素,将内容添加到body
for(var i in records){
//2.1.创建tr对象
var tr=$("<tr></tr>");
tr.data("user",records[i]);
//2.2创建td元素
var tds=createTds(records[i]);
//2.3将tr追加到tr中
tr.append(tds);
//2.4将tr追加到tbody中
tBody.append(tr);
}
}
function createTds(data){
console.log("data.id="+data.id);
var tds=
"<td><input type='radio' name='checkItem' value='"+data.id+"'/></td>"+
"<td>"+data.username+"</td>"+
"<td>"+data.sysDept.name+"</td>"+
"<td>"+data.email+"</td>"+
"<td>"+data.mobile+"</td>"+
"<td>"+(data.valid?'启用':'禁用')+"</td>"+
"<td>"+data.createdTime+"</td>"+
"<td>"+data.modifiedTime+"</td>"+
"<td><button type='button' class='btn btn-default btn-valid'>"+(data.valid?'禁用':'启用')+"</button></td>";
return tds;
}

 10.用户列表页面数据异步查询实现

业务描述

  1. 查询按钮事件注册

  2. 查询按钮事件处理函数定义(执行查询操作,重用doGetObjects函数)

          a.初始化当前页码

          b.获取表单参数

          c.异步提交请求.

代码实现:

$(document).ready(function(){
$("#pageId").load("doPageUI.do",doGetObjects);
$(".input-group-btn")
.on("click",".btn-search",doQueryObjects)
});
function doQueryObjects(){
//1.初始化分页pageCurrent的值
$("#pageId").data("pageCurrent",1);
//2.异步查询
doGetObjects();
}

效果:


 3.用户禁用启用操作实现

5.服务端实现

11.Dao接口实现

业务描述

定义一个方法,根据业务层传入的数据,对用户信息执行禁用或启用操作.

业务实现(在SysUserDao中定义禁用启用方法)

  1. 方法名 validById

  2. 参数列表(Integer id,Integer valid,String modifiedUser)

  3. 返回值 int

代码实现:  

int validById(
@Param("id")Integer id,
@Param("valid")Integer valid,
@Param("modifiedUser")String modifiedUser);

 12.Mapper文件实现

业务描述

在SysUserMapper中定义与SysUserDao中方法对应的禁用启用元素

元素定义

  1. 元素名 update

  2. 元素id validById

  3. 元素参数(不写)

  4. SQL (update sys_users set ... where ....)

代码实现:

<update id="validById">
update sys_users
set valid=#{valid},
modifiedUser=#{modifiedUser},
modifiedTime=now()
where id=#{id}
</update>

 13.Service接口实现

业务描述

  1. 接收控制层数据(id,valid,modifiedUser)

  2. 对数据进行合法性验证

  3. 调用dao层方法执行更新操作(禁用或启用)

  4. 对结果进行验证,并返回.

业务实现(在SysUserService接口及实现类定义禁用启用方法)

  1. 方法名 validById

  2. 方法参数列表(Integer id,Integer valid,String modifiedUser)

  3. 返回值int

代码实现

接口实现类中方法的实现。

@Override
public int validById(Integer id,Integer valid,String modifiedUser) {
//1.合法性验证
if(id==null||id<=0)
throw new ServiceException("参数不合法,id="+id);
if(valid!=1&&valid!=0)
throw new ServiceException("参数不合法,valie="+valid);
if(StringUtils.isEmpty(modifiedUser))
throw new ServiceException("修改用户不能为空");
//2.执行禁用或启用操作
int rows=0;
try{
rows=sysUserDao.validById(id, valid, modifiedUser);
}catch(Throwable e){
e.printStackTrace();
//报警,给维护人员发短信
throw new ServiceException("底层正在维护");
}
//3.判定结果,并返回
if(rows==0)
throw new ServiceException("此记录可能已经不存在");
return rows;
}

 14.Controller类实现

业务描述

  1. 接收客户端请求数据(id,valid)

  2. 调用业务层对应方法执行禁用启用操作

  3. 封装响应数据,并返回

业务实现(SysUserController类中定义禁用启用方法)

  1. 方法名 doValidById

  2. 方法参数 (Integer id,Integer valid)

  3. 方法返回值 JsonResult

  4. 方法映射@RequestMapping("doValidById")

代码实现

@RequestMapping("doValidById")
@ResponseBody
public JsonResult doValidById(
Integer id,
Integer valid){
sysUserService.validById( id, valid,"admin");//"admin"用户将来是登陆用户
return new JsonResult("update ok");
}

 6.客户端实现

15.用户列表页面禁用按钮事件处理

业务描述

  1. 按钮事件注册(btn-valid)

  2. 按钮事件处理函数定义(doValidById)

代码实现

$(document).ready(function(){
…
$("#tbodyId")
.on("click",".btn-valid",doValidById)
});
function doValidById(){
var url="user/doValidById.do";
var rowData=$(this).parents("tr").data("rowData");
var valid=$(this).parents("tr").data("valid");
var params={"id":rowData.id,"valid":rowData.valid?0:1};
var btn=$(this);
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doGetObjects();//重新查询刷新
//优化方式实现
doEditRow(btn,valid);
}else{
alert(result.message);
}
})   
}   

局部刷新(扩展实现)

function doEditRow(obj,valid){
console.log(obj);
//1.修改按钮上的内容
$(obj).html(valid?"启用":"禁用");
//2.获取当前行tr对象,然后重新绑定新的值
var tr=$(obj).parents("tr");
tr.data("valid",valid?0:1);
//3.修改tr中第5个td中的内容(查询API文档)
tr.find("td:eq(5)").text(valid?"禁用":"启用");
}

效果:


4. 用户添加页面呈现

7.服务端实现

16.用户Controller实现

业务描述

在SysUserController对象中添加一个方法,返回编辑页面

业务实现(方法定义)

  1. 方法名 editUI

  2. 返回值 String

  3. 参数列表()

  4. 方法映射 @RequestMapping("doUserEditUI")

代码实现:

@RequestMapping("doUserEditUI")
public String doUserEditUI(){
return "sys/user_edit";
}

 8.客户端实现

17.用户列表页面添加按钮事件处理

业务描述

  1. 添加按钮事件注册

  2. 添加按钮事件处理(核心是异步加载页面)

代码实现 

$(document).ready(function(){
$(".input-group-btn")
.on("click",".btn-add ",doLoadEditUI)
});

定义页面加载方法,后续修改时也调用此方法,然后进行修改完善。

function doLoadEditUI(){
//1.判定点击的对象
var title;
if($(this).hasClass("btn-add")){
title="添加用户";
doLoadPage(title);
}else if($(this).hasClass("btn-update")){
title="修改用户";
var id=doGetCheckedId();
console.log("id="+id)
if(!id){
alert("请先选择");
return;
}
//基于id进行查询并加载编辑页面
doFindObjectById(id,title);
}
}

function doFindObjectById(id,title){
//1.params
var params={"id":id};
//2.url
var url="user/doFindObjectById.do";
//3.ajax request
$.getJSON(url,params,function(result){//JsonResult
if(result.state==1){
$("#mainContentId").data("rowData",result.data);
doLoadPage(title);  
}else{
alert(result.message);  
}
});
}

function doLoadPage(title){
var url="user/doUserEditUI.do"
$("#mainContentId").load(url,function(){
$(".box-title").html(title);
})
}a

5.用户添加页面菜单数据呈现

核心业务

页面加载完成以后,启动异步请求从服务端获取角色信息初始化到页面上。

9.服务端实现

18.Vo对象定义

业务描述

借助此对象封装角色id,角色名称.

业务实现(定义类并实现序列化接口,添加版本id,提供set/get)

  1. 包名 com.jtmon.vo

  2. 类名 CheckBox

  3. 字段 (Integer id,String name)

代码实现:

public class CheckBox implements Serializable{
private static final long serialVersionUID = 2031967811425337153L;
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "CheckBox [id=" + id + ", name=" + name + "]";
}
}

 19.Dao接口实现

业务描述

在SysRoleDao接口中添加一个查询角色ID,角色名的方法

业务实现(方法定义)

  1. 方法名 findObjects

  2. 方法参数()

  3. 方法返回值 List<CheckBox>

代码实现:

List<CheckBox> findObjects();

 20.Mapper文件实现

业务描述

在SysRoleMapper文件中添加查询角色id和名字的元素

业务实现

  1. 元素名 select

  2. 元素id findObjects

  3. 结果类型 com.jtmon.vo.CheckBox

  4. SQL (select id,name from sys_roles)

代码实现

<select id="findObjects"
resultType="com.jtmon.vo.CheckBox">
select id,name
from sys_roles
</select>

 21.Service接口实现

业务描述

通过SysRoleDao中的方法获取所有的角色id,角色名

业务实现(在SysRoleService接口及实现中定义方法)

  1. 方法名 findObjects

  2. 参数列表 ()

  3. 返回值 List<CheckBox>

代码实现:

@Override
public List<CheckBox> findObjects() {
return sysRoleDao.findObjects();
}

22.Controller实现

业务描述 1 )依据请求获取角色id和角色名 2) 封装数据,并返回. 业务实现(方法定义)
  1. 方法名 doFindObjects
  2. 参数列表()
  3. 返回值 JsonResult
  4. 方法映射 @RequestMapping("doFindObjects")
代码实现:
@RequestMapping("doFindObjects")
@ResponseBody
public JsonResult doFindObjects(){
return new JsonResult(
sysRoleService.findObjects(),"query ok");
}

 10.客户端实现

23.用户编辑页面实现

业务描述 页面加载完成 , 启动异步任务加载角色信息,初始化页面角色内容. 代码实现
$(document).ready(function(){
doLoadRoles();
//事件注册
$(".box-footer")
.on("click",".btn-cancel",doCancel)
.on("click",".btn-save",doSaveOrUpdate);
$("#treeLayer")
.on("click",".btn-cancel",doHideTree)
.on("click",".btn-confirm",doConfirm);
$(".form-horizontal")
.on("click",".load-sys-dept",doLoadZTreeNodes);
}
//页面加载完成,加载角色信息
function doLoadRoles(){
var url="role/doFindObjects.do"
$.getJSON(url,function(result){
if(result.state==1){
doInitPageRoles(result.data);
doInitFormData();//修改时
}else{
alert(result.message);
}
})
}
//初始化表单角色数据
function doInitPageRoles(data){
//1.获取角色要显示的位置对象
var div=$("#rolesId");
div.empty();
//2.迭代数据,将数据追加到div
var input="<input type='checkbox' name='roleId' value='[id]'>[name]"
for(var i in data){
//记住每次replace时都会产生一个新的字符串对象
var newInput=
input.replace("[id]",data[i].id)
.replace("[name]",data[i].name);
div.append(newInput)
}
}
function doInitFormData(){
var data=$("#mainContentId").data("rowData");
if(!data)return;
console.log(data);
//初始化用户信息
$("#usernameId").val(data.user.username);
$("#deptId").val(data.user.sysDept.name);
console.log("data.user.sysDept.idss="+data.user.sysDept.id);
$("#deptId").data("deptId",data.user.sysDept.id);
$("#emailId").val(data.user.email);
$("#phoneId").val(data.user.mobile);
//初始化用户角色信息
var ids=data.roleIds;
for(var i in ids){
$("#rolesId input[value='"+ids[i]+"']")
.prop("checked",true);
}
}
function doHideTree(){
$("#treeLayer").css("display","none");
}
//确定按钮
function doConfirm(){
//1.获取选中的记录(id,name);
var selectedNodes=zTree.getSelectedNodes();
var node=selectedNodes[0];
//2.将id和name填写或绑定在具体对象上
$("#deptId").val(node.name);
console.log("node.id="+node.id)
$("#deptId").data("deptId",node.id)
//3.隐藏zTree对应的Div
doHideTree();
}
function doLoadZTreeNodes(){
var url="dept/doFindZTreeNodes.do";
$("#treeLayer").css("display","block");
$.getJSON(url,function(result){
if(result.state==1){
zTree = $.fn.zTree.init($("#zTreeId"),setting,result.data);
}else{
alert(result.message);
}
});
}
效果: 点击上级部门初始化部门数据:

6.用户数据保存实现

11.服务端实现

24.Dao 接口实现

业务描述
  1. 接收业务层数据(SysUser),并将实体对象数据保存到数据库
  2. 接收用户和角色的关系数据,并将数据写入到中间关系表中.
业务实现 1)在 SysUserDao 中添加插入方法 a.方法名 insertObject b.参数列表(SysUser entity ) c.返回值 int 2)在SysUserRoleDao接口中定义方法 a.方法名 insertObject b.参数列表( Integer userId, Integer [] roleIds) c.返回值 int 代码实现
/**
* 负责将用户信息写入到数据库
* @param entity
* @return
*/
int insertObject(SysUser entity);
/**
* 负责将用户与角色的关系数据写入到数据库
* @param userId 用户id
* @param roleIds 多个角色id
* @return
*/
int insertObject(
@Param("userId")Integer userId,
@Param("roleIds")Integer[] roleIds);

 25.Mapper文件实现

业务描述
  1. 基于SysUserDao中insertObject方法定义SQL映射元素
  2. 基于SysUserRoleDao中insertObject方法定义SQL映射元素
业务实现:
  1. SysUserMapper 中定义insert元素,实现向对应表中写入数据
a.元素名 insert b.元素 id insertObject c.参数类型 com.jt.sys.entity.SysUser d.SQL (insert into sys_users (…) values (…));

 2.在SysUserRoleMapper中定义insert元素,实现向对应表中写入数据

a.元素名 insert b.元素 id insertObject c.SQL (insert into sys_user _ roles (…) values (…)); 代码实现 S ysUserMapper 中元素定义
<insert id="insertObject"
parameterType="sysUser"
useGeneratedKeys="true"
keyProperty="id">
insert into sys_users
(username,password,email,mobile,salt,valid,
createdTime,modifiedTime,createdUser,modifiedUser)
values
(#{username},#{password},#{email},#{mobile},#{salt},#{valid},
now(),now(),#{createdUser},#{modifiedUser})
</insert>
SysUser RoleMapper 中元素定义
<insert id="insertObject">
insert into sys_user_roles
(user_id,role_id)
values
<foreach collection="roleIds" separator="," item="item">
(#{userId},#{item})
</foreach>
</insert>

 26.用户Service实现

业务描述
  1. 接收客户端数据,并进行合法验证
  2. 将数据保存到数据库(用户信息,用户角色关系信息)
  3. 验证结果,并返回.
业务实现 SysUserService 接口及实现类定义方法
  1. 方法名 saveObject
  2. 参数列表(SysUser entity,Integer[] roleIds)
  3. 返回值 int
代码实现
@Override
public int saveObject(SysUser entity, Integer[] roleIds) {
//1.验证数据合法性
if(entity==null||roleIds==null)
throw new ServiceException("数据参数不能为空");
if(StringUtils.isEmpty(entity.getUsername()))
throw new ServiceException("用户名不能为空");
if(StringUtils.isEmpty(entity.getPassword()))
throw new ServiceException("密码不能为空");
//2.将数据写入数据库
String salt=UUID.randomUUID().toString();
entity.setSalt(salt);
//加密(先了解,讲shiro时再说)
SimpleHash sHash=
new SimpleHash("MD5",entity.getPassword(), salt);
entity.setPassword(sHash.toString());
int rows=sysUserDao.insertObject(entity);
sysUserRoleDao.insertObject(
entity.getId(),
roleIds);//"1,2,3,4";
//3.返回结果
return rows;
}
说明:使用SimpleHash时,要添加一个shiro框架依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>

 27.用户Controller实现

业务实现
  1. 获取请求数据(用户信息,关系数据)
  2. 调用业务层方法将数据写入到数据库
  3. 封装数据,并返回
业务实现
  1. 方法名 doSaveObject
  2. 参数名(SysUser entity,String roleIds)
  3. 返回值 JsonResult
  4. 映射 @RequestMapping("doSaveObject");
代码实现:
@RequestMapping("doSaveObject")
@ResponseBody
public JsonResult doSaveObject(
SysUser entity,
Integer[] roleIds){
sysUserService.saveObject(entity,roleIds);
return new JsonResult("save ok");
}

 12.客户端实现

28.用户编辑页面事件处理

业务描述
  1. cancel 事件处理(点击 cancel 按钮退出编辑页面加载列表页面)
  2. save 事件处理(将业务数据提交到服务端,并关闭当页面,加载列表页面)
代码实现: C ancel按钮事件处理
$(document).ready(function(){
doLoadSysRoles();
$(".box-footer").on("click",".btn-cancel",doCancel);
});
function doCancel(){
$("#mainContentId").load("user/doUserListUI.do",function(){
$("#mainContentId").removeData();
});
}
save按钮事件处理
function doSaveOrUpdate(){
var rowData=$("#mainContentId").data("rowData");
//1.获取表单数据
var params=doGetEditFormData();
if(rowData)params.id=rowData.user.id;
//2.发起异步请求
var insertUrl="user/doSaveObject.do";
var updateUrl="user/doUpdateObject.do";
var url=rowData?updateUrl:insertUrl;
console.log(params);
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
})
}

获取页面表单数据

function doGetEditFormData(){
//获取用户输入的数据
var params={
username:$("#usernameId").val(),
password:$("#passwordId").val(),
email:$("#emailId").val(),
mobile:$("#phoneId").val(),
deptId:$("#deptId").data("deptId")
}
//获取选择的角色
var roleIds=new Array();
$("#rolesId input[name='roleId']")
.each(function(){
if($(this).prop("checked")){
roleIds.push($(this).val());
}
});
params.roleIds=roleIds.toString();
return params;
}

7.用户页面修改呈现

核心业务:
  1. 根据用户 id 查找用户以及对应的角色信息
a.方案 1: 执行两次查询(本阶段采用) b.方案 2: 执行一次关联查询(后续扩展)

 2.验证此用户是否还存在

3.假如存在则将信息呈现在编辑页面上.

 13.服务端实现

29.用户DAO实现

业务描述 根据用户 id 查询用户信息 业务实现(在 SysUserDao 中定义根据id查询用户信息的方法)
  1. 方法名: findObjectById
  2. 参数列表:Integer id
  3. 返回值: SysUser
代码实现  
SysUser findObjectById(Integer id);

30.用户Mapper实现

业务描述 SysUserMapper 中添加根据id进行用户信息查找的相关元素 业务实现
  1. 元素名 select
  2. 元素 id findObjectById
  3. 参数类型 int
  4. 结果类型 com.jt.sys.entity.SysUser
  5. sql 定义 (select * from from sys_users where user_id=?)
代码实现:
<select id="findObjectById"
parameterType="int"
resultType=" com.jt.sys.entity.SysUser">
select *
from sys_users  
where id=#{id}    
</select>

 31.用户角色DAO实现

业务描述 根据用户id查询角色id 业务实现(在 SysUserRoleDao 中定义相关方法)
  1. 方法名 findRoleIdsByUserId
  2. 参数列表 Integer userId
  3. 返回值 List<Integer>
代码实现
List<Integer> findRoleIdsByUserId(
Integer userId);

 32.用户角色Mapper实现

业务描述 SysUserRoleMapper 中定义基于用户id查找角色id的相关元素 业务实现
  1. 元素名    select
  2. 参数列表 int
  3. 结果类型 int
  4. SQL 语句 (select role_id from sys_user_roles where user_id=?)
代码实现
<select id="findRoleIdsByUserId"
resultType="int">
select role_id
from sys_user_roles
where user_id=#{userId}
</select>

 33.用户Service实现

业务描述
  1. 获取控制层数据(userId),并对其进行合法性验证
  2. 调用 dao 方法根据用户id查询用户信息以及对应的角色信息
  3. 对数据进行封装,并返回.
业务实现(在 SysUserService 接口及实现类中定义相关方法)
  1. 方法名 findObjectById
  2. 参数列表(Integer userId)
  3. 返回值 Map<String,Object>
代码实现:
@Override
public Map<String, Object> findObjectById(
Integer userId) {
//1.合法性验证
if(userId==null||userId<=0)
throw new ServiceException(
"参数数据不合法,userId="+userId);
//2.业务查询
SysUser user=
sysUserDao.findObjectById(userId);
if(user==null)
throw new ServiceException("此用户已经不存在");
List<Integer> roleIds=
sysUserRoleDao.findRoleIdsByUserId(userId);
//3.数据封装
Map<String,Object> map=new HashMap<>();
map.put("user", user);
map.put("roleIds", roleIds);
return map;
}

 34.用户Controller实现

业务描述
  1. 接收客户端请求数据 (userId)
  2. 调用业务方法查询用户以及对应角色信息
  3. 封装数据,并返回
业务实现(在 SysUserController 中定义相关方法)
  1. 方法名 doFindObjectBy Id
  2. 参数列表 (Integer userId)
  3. 返回值 JsonResult
  4. 映射 @RequestMapping("doFindObjectById");
代码实现
@RequestMapping("doFindObjectById")
@ResponseBody
public JsonResult doFindObjectById(
Integer userId){
Map<String,Object> map=
sysUserService.findObjectById(userId);
return new JsonResult(map, "query ok");
}

 14.客户端实现

核心业务
  1. 点击修改时加载编辑页面
  2. 在编辑页面上呈现用户以及对应的额角色信息

  35.用户列表页面业务实现

业务描述
  1. 修改按钮事件注册
  2. 定义事件处理函数(获取 id, 根据id执行查询,绑定结果 )
  3. 异步加载编辑页面.
代码实现:
$(document).ready(function(){
$(".input-group-btn")
.on("click",".btn-add,.btn-update",doLoadEditUI)
});
function doLoadEditUI(){
//1.判定点击的对象
var title;
if($(this).hasClass("btn-add")){
title="添加用户";
doLoadPage(title);
}else if($(this).hasClass("btn-update")){
title="修改用户";
var id=doGetCheckedId();
console.log("id="+id)
if(!id){
alert("请先选择");
return;
}
//基于id进行查询并加载编辑页面
doFindObjectById(id,title);
}
}
function doGetCheckedId(){
return $("tbody input[name='radioId']:checked").val();
}
function doFindObjectById(id,title){
//1.params
var params={"id":id};
//2.url
var url="user/doFindObjectById.do";
//3.ajax request
$.getJSON(url,params,function(result){//JsonResult
if(result.state==1){
$("#mainContentId").data("rowData",result.data);
doLoadPage(title);
}else{
alert(result.message);
}
});
}

 36.用户编辑页面数据初始化

页面描述
  1. 呈现页面,加载角色信息.
  2. 角色信息呈现结束 , 获取页面绑定的数据,通过数据初始化页面.
代码实现:
$(document).ready(function(){
doLoadRoles();
}
页面加载完成,加载角色信息
function doLoadRoles(){
var url="role/doFindObjects.do"
$.getJSON(url,function(result){
if(result.state==1){
doInitPageRoles(result.data);
doInitFormData();//修改时
}else{
alert(result.message);
}
})
}

修改时通过此方法初始化数据

function doInitPageRoles(data){
//1.获取角色要显示的位置对象
var div=$("#rolesId");
div.empty();
//2.迭代数据,将数据追加到div
var input="<input type='checkbox' name='roleId' value='[id]'>[name]"
for(var i in data){
//记住每次replace时都会产生一个新的字符串对象
var newInput=
input.replace("[id]",data[i].id)
.replace("[name]",data[i].name);
div.append(newInput)
}
}
function doInitFormData(){
var data=$("#mainContentId").data("rowData");
if(!data)return;
console.log(data);
//初始化用户信息
$("#usernameId").val(data.user.username);
$("#deptId").val(data.user.sysDept.name);
console.log("data.user.sysDept.idss="+data.user.sysDept.id);
$("#deptId").data("deptId",data.user.sysDept.id);
$("#emailId").val(data.user.email);
$("#phoneId").val(data.user.mobile);
//初始化用户角色信息
var ids=data.roleIds;
for(var i in ids){
$("#rolesId input[value='"+ids[i]+"']")
.prop("checked",true);
}
}

8.用户编辑页面数据更新

核心业务
  1. 获取用户表单数据
  2. 将表单数据更新到数据库.

 15.服务端实现

37.用户Dao实现

业务描述
  1. 接收业务层数据(SysUser)
  2. 将数据持久化到数据库.
业务实现(在 SysUserDao 中添加更新数据的方法)
  1. 方法名 updateObject
  2. 参数列表 (SysUser user)
  3. 返回值 int
代码实现:
int updateObject(SysUser entity);

38.用户Mapper实现

业务描述 基于 SysUserDao 中的更新方法,定义更新元素 元素定义
  1. 元素名 updateObject
  2. 参数类型  com.jt.sys.entity.SysUser
  3. sql 语句 (update sys_users set ... where id=?)
代码实现:
<update id="updateObject" parameterType="sysUser">
update sys_users
<set>
<if test="username!=null and username!=''">
username=#{username},
</if>
<if test="password!=null and password!=''">
password=#{password},
salt=#{salt},
</if>
<if test="email!=null and email!=''">
email=#{email},
</if>
<if test="mobile!=null and mobile!=''">
mobile=#{mobile},
</if>
<if test="modifiedUser!=null and modifiedUser!=''">
modifiedUser=#{modifiedUser},
</if>
modifiedTime=now()
</set>
where id=#{id}     
</update>

 39.用户角色Dao实现

页面描述 更新用户数据时,涉及到的关系数据,一般先删除,再添加,所以 在关系表对应的dao中我们需要先根据用户id删除对应的 用户角色关系数据. 业务实现(在 SysUserRoleDao 中定义删除方法 )
  1. 方法名: deleteObjects;
  2. 参数列表: (Integer userId);
  3. 返回值: int
代码实现:     
 int deleteObjects(Integer userId);

40.用户角色Mapper实现

业务描述 基于 SysUserRoleDao 接口中删除方法,定义对应的删除元素. 业务实现
  1. 元素名: delete
  2. 元素id deleteObjects
  3. 参数 :int
  4. sql 定义: delete from sys_user_roles where user_id=?
代码实现:
<delete id="deleteObjects"
parameterType="int">
delete from sys_user_roles
where user_id=#{userId}
</delete>

 41.用户Service实现

业务描述
  1. 获取控制层数据,并进行合法验证.
  2. 更新用户自身信息
  3. 删除用户角色关系数据
  4. 重新添加新的用户角色关系数据
业务实现:(在 SysUserService 接口及实现类中定义相关方法)
  1. 方法名: updateObject
  2. 参数列表:(SysUser entity,String roleIds)
  3. 返回值 : int
代码实现:
@Override
public int updateObject(SysUser entity,
String roleIds) {
//1.合法验证
if(entity==null)
throw new ServiceException("用户信息不能为空");
if(StringUtils.isEmpty(entity.getUsername()))
throw new ServiceException("用户名不能为空");
//用户名已经存在的验证,尝试自己实现.
if(StringUtils.isEmpty(roleIds))
throw new ServiceException("用户必须选一个角色");
if(!StringUtils.isEmpty(entity.getPassword())){
//对密码加密
String salt=UUID.randomUUID().toString();
SimpleHash hash=//shiro
new SimpleHash(
"MD5",
entity.getPassword(),
salt);
entity.setPassword(hash.toString());
}
//2.更新数据
int rows=0;
try{
rows=sysUserDao.updateObject(entity);
sysUserRoleDao.deleteObjects(entity.getId());
sysUserRoleDao.insertObject(
entity.getId(),roleIds.split(","));
}catch(Throwable e){
e.printStackTrace();
//发起报警信息
throw new ServiceException("服务端现在异常,请稍后访问");
}
//3.返回结果
return rows;
}

42.用户Controller实现

业务描述
  1. 接收客户端请求数据
  2. 调用业务层方法,将请求数据更新到数据库
  3. 封装数据并返回.
业务实现:(在 SysUserController 中定义相关方法)
  1. 方法名 doUpdateObject
  2. 参数列表(SysUser entity,String roleIds);
  3. 返回值 JsonResult
  4. 映射 @RequestMapping("doUpdateObject")
代码实现:    
@RequestMapping("doUpdateObject")
@ResponseBody
public JsonResult doUpdateObject(
SysUser entity,String roleIds){
sysUserService.updateObject(entity,
roleIds);
return new JsonResult("update ok");
}

 16.客户端实现

核心业务 点击保存按钮时,将用户输入的表单数据,更新到数据库.

43.用户编辑页面事件处理.

业务描述 1 ) 添加和更新操作共用一个页面. 2) 点击保存按钮将数据更新到服务端. 代码实现.
function doSaveOrUpdate(){
var rowData=$("#mainContentId").data("rowData");
//1.获取表单数据
var params=doGetEditFormData();
if(rowData)params.id=rowData.user.id;
//2.发起异步请求
var insertUrl="user/doSaveObject.do";
var updateUrl="user/doUpdateObject.do";
var url=rowData?updateUrl:insertUrl;
console.log(params);
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
})
}

function doGetEditFormData(){
//获取用户输入的数据
var params={
username:$("#usernameId").val(),
password:$("#passwordId").val(),
email:$("#emailId").val(),
mobile:$("#phoneId").val(),
deptId:$("#deptId").data("deptId")
}
//获取选择的角色
var roleIds=new Array();
$("#rolesId input[name='roleId']")
.each(function(){
if($(this).prop("checked")){
roleIds.push($(this).val());
}
});
params.roleIds=roleIds.toString();
return params;
}

function doCancel(){
$("#mainContentId").removeData("id");
$("#mainContentId").load("user/doUserListUI.do");
}

9.总结

17.重点和难点分析

用户与角色关系数据的插入 用户与角色关系数据的更新 用户密码的加密设置

18.常见FAQ


 1

                                          齐雷

day06:京淘-权限管理子系统-Shiro安全框架

1.    Shiro安全框架简介                                       2 1.1.    Shiro 概述                                           2 1.2.    Shiro 概要架构                                    2 1.3.    Shiro 详细架构                                    2 2.    Shiro 认证与授权分析                                  4 2.4.    Shiro 认证流程                                    4 2.5.    Shiro 授权流程                                    4 3.    Shiro 框架基本配置                                      5 3.6.    Shiro 基本依赖配置                             5 3.7.    Shiro 基本组件配置                             6 3.8.    Shiro 核心过滤器配置                          7 4.    Shiro 框架认证流程应用实现                          8 4.9.    服务端实现                                             8 4.9.1.    Dao接口实现                                  8 4.9.2.    Mapper元素定义                            8 4.9.3.    Service 接口实现                             9 4.9.4.    Controller类实现                             10 4.10.    客户端实现                                              11 4.10.5.    编写用户登陆页面                           11 4.10.6.    异步登陆操作实现                           11 5.    Shiro 框架授权流程应用实现                              12 5.11.    服务端实现                                               12 5.11.7.    Dao 实现                                        12 5.11.8.    Mapper实现                                   13 5.11.9.    Service实现                                   14 5.12.    授权检测实现                                         15 6.    Shiro框架应用增强                                            16 6.12.10.    Shiro 缓存配置                           16 6.12.11.    Shiro 记住我                              16 7.    总结                                                                 17 7.13.    重点和难点分析                                    17 7.14.    常见FAQ                                              17

1.Shiro安全框架简介

1.Shiro 概述

Shiro是apache旗下一个轻量级开源安全框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,授权、加密、会话管理等功能,组成了一个通用的安全认证框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统开发成本。 课后了解:Spring security 安全框架(相对shiro表现的更加重量级)

2.Shiro 概要架构

在概念层,Shiro 架构包含三个主要的理念: Subject,SecurityManager 和 Realm。 概要分析: 1) Subject : 待认证和授权的用户主题(例如封装了用户和密码的对象) 2) SecurityManager : Shiro 的核心安全管理器对象 3) Realm:负责获取认证信息,授权信息的一个业务对象.

3.Shiro 详细架构

Shiro 的核心架构思想如下图所示: 通过Shiro框架进行权限管理时,要涉及到的一些核心对象,主要包括: 认证管理对象 , 授权管理对象 , 会话管理对象 , 缓存管理对象,加密管理对象 以及Real m 管理对象(领域对象:负责处理认证和授权领域的数据访问题 )
  1. Subject :与软件交互的一个特定的实体(用户、第三方服务等)。
  2. SecurityManager :Shiro 的核心,用来协调管理组件工作。
  3. Authenticator :负责执行认证操作
  4. Authorizer :负责授权检测
  5. SessionManager :负责创建并管理用户 Session 生命周期,提供一个强有力的 Session 体验。
  6. SessionDAO :代表 SessionManager 执行 Session 持久(CRUD)动作,它允许任何存储的数据挂接到 session 管理基础上。
  7. CacheManager: 提供创建缓存实例和管理缓存生命周期的功能
  8. Cryptography :提供了加密方式的设计及管理。
  9. Realms :是shiro和你的应用程序安全数据之间的桥梁。  

2.Shiro 认证与授权分析

4.Shiro 认证流程

用户访问系统资源时假如这个资源不能匿名访问就需要此用户进行身份认证,在Shiro框中的认证流程如下: 具体流程分析如下: 1)系统调用subject的login方法将用户信息提交给SecurityManager 2)SecurityManager将认证操作委托给认证器对象Authenticator 3)Authenticator借助认证策略对象将身份信息传递给Realm。 4)Realm访问数据库获取用户信息然后对信息进行封装并返回。 5)Authenticator对象对realm返回的信息进行身份认证。

5.Shiro 授权流程

用户访问系统资源时,首先要检测此资源是否允许匿名访问,假如不允许访问则需要先对用户信息进行认证,认证通过以后能否访问对应的资源还要看是否有访问权限,对用户权限进行检测并授予访问权限的过程称之为授权.Shiro框架中的授权流程如下: 1)系统调用subject相关方法将用户信息(例如 isPermitted )递交给SecurityManager 2)SecurityManager将权限检测操作委托给Authorizer对象 3)Authorizer将用户对应权限信息的获取委托给realm对象 4)Realm访问数据库获取用户权限信息并封装返回。 5)Authorizer对用户授权信息进行判定并授权。


3.Shiro 框架基本配置

6.Shiro 基本依赖配置

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>

7.Shiro 基本组件配置

Spring 集成shiro框架的基本应用,例如在AppRootConfig配置类中添加如下配置。
/** 
* 配置shiro核心权限管理对象 
* @param userRealm 
* @return 
*/
@Bean("securityManager")
public DefaultWebSecurityManager  newDefaultWebSecurityManager(
AuthorizingRealm userRealm){
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
//此时必须保证realm对象已经存在了
sManager.setRealm(userRealm);
return sManager;
}
手动引入这个包
import 
org.apache.shiro.mgt.SecurityManager;
/** 
* 配置Shiro的过滤器Bean工厂 
* @param securityManager 
* @return 
*/
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean newShiroFilterFactoryBean(
SecurityManager securityManager){//shiro 包
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
//当此用户是一个非认证用户,需要先登陆进行认证
bean.setLoginUrl("/doLoginUI.do");
LinkedHashMap<String,String> fcMap=
new LinkedHashMap<>();
fcMap.put("/bower_components/**","anon");//anon表示允许匿名访问
fcMap.put("/build/**", "anon");
fcMap.put("/dist/**","anon");
fcMap.put("/plugins/**","anon");
fcMap.put("/doLogin.do","anon");
fcMap.put("/doLogout.do ","logout");
fcMap.put("/**", "authc");//必须授权才能访问
bean.setFilterChainDefinitionMap(fcMap);
return bean;
}
/**
* 管理shiro框架组建的生命周期
* @return
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor newLifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
* 配置负责为Bean对象(需要授权访问的方法所在的对象)
* 创建代理对象的Bean组件
* @return
*/
@DependsOn(value="lifecycleBeanPostProcessor")
@Bean
public DefaultAdvisorAutoProxyCreator newDefaultAdvisorAutoProxyCreator(){
return new DefaultAdvisorAutoProxyCreator();
}

/**
* 配置授权属性属性应用对象(在执行授权操作时需要用到此对象)
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor newAuthorizationAttributeSourceAdvisor(
SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor bean=
new AuthorizationAttributeSourceAdvisor();
bean.setSecurityManager(securityManager);
return bean;
}
/**
* @PropertySource("classpath:configs.properties")
* 这种文件的注入方式Spring只会加载一次properties文件。当引入Shiro
* 并对其进行配置之后,由于Shiro的底层含有properties文件,Spring
* 可能先构建Properties对象加载了这个文件,而一旦是Spring识别到内
* 部含有Properties对象,就不会再加载其余的properties文件.所以
* jdbc的相关配置并未被加载,进而导致数据库连接异常。
*
* 解决方式是用Spring整合PropertySourcesPlaceholderConfigurer对象,
* 启用此对象可以加载多个properties文件。
* @return
*/
@Bean
public PropertySourcesPlaceholderConfigurer getPropertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}

8.Shiro 核心过滤器配置

在注解启动类中 com / jt /common/ config /WebAppInitializer.java 重写onStart up 方法,完成过滤器的注册   
@Override
public void onStartup(ServletContext servletContext)
throws ServletException {
System.out.println("onStartup");
//super.onStartup(servletContext);
registerContextLoaderListener(servletContext);
registerFilter(servletContext);
registerDispatcherServlet(servletContext);
}
private void registerFilter(ServletContext servletContext) {
//注册Filter对象
//什么时候需要采用此方式进行注册?
//项目没有web.xml并且此filter不是自己写的
FilterRegistration.Dynamic dy=
servletContext.addFilter("filterProxy",
DelegatingFilterProxy.class);
dy.setInitParameter("targetBeanName","shiroFilterFactoryBean");
dy.addMappingForUrlPatterns(
null,//EnumSet<DispatcherType>
false,"/*");//url-pattern
}

 4.密码错误异常

com / jt /common/controller/GlobalExceptionHandler.java类中增加以下方法
/**
* 密码异常
* @param e
* @return
*/
@ExceptionHandler(AuthenticationException.class)
@ResponseBody
public JsonResult doHandleAuthenticationException(
AuthenticationException e){
if(e instanceof IncorrectCredentialsException) {
return new JsonResult(0,"密码不正确");
}
return new JsonResult(e);
}

com.jtmon.vo.JsonResult类中增加以下构造方法

/**密码异常时时调用*/
public JsonResult(Integer state ,String message){
this.state=state;
this.message=message;
}
效果: 出现的bug 修改后的用户密码还是无法登陆? 更改密码时将随机生成的盐值也添加到用户对象中
@Override
public int updateObject(SysUser entity, Integer[] roleIds) {
//1.校验
if(entity==null) throw new IllegalArgumentException("更新对象不能为空");
if(StringUtils.isEmpty(entity.getUsername())) throw new IllegalArgumentException("用户名不能为空");
if(roleIds==null||roleIds.length==0) throw new IllegalArgumentException("需要为用户分配角色");

if(!StringUtils.isEmpty(entity.getPassword())){
String salt=UUID.randomUUID().toString();
entity.setSalt(salt);
SimpleHash sh= new SimpleHash("MD5",entity.getPassword(),salt);
entity.setPassword(sh.toString());
}
//2.更新
int rows=sysUserDao.updateObject(entity);
sysUserRoleDao.deleteObjectsByUserId(entity.getId());
sysUserRoleDao.insertObject(entity.getId(), roleIds);
//3.返回
return rows;
}

 4.Shiro 框架认证流程应用实现

本讲的shiro应用主要讲解shiro是集成到spring如何实现权限控制

9.服务端实现

1.Dao接口实现

业务描述 在SysUserDao中根据用户名获取用户对象 业务实现(根据用户名查询用户对象的方法定义)
  1. 返回值SysUser
  2. 方法名findUserByUserName
  3. 参数列表(String username)
代码实现:
SysUser findUserByUserName(String username);

2.Mapper元素定义

根据SysUserDao中定义的方法,添加元素定义
<select id="findUserByUserName"
resultType="sysUser">
select *
from sys_users 
where username=#{username}
</select>

 3.Service 接口实现

业务描述 本模块的service可以借助realm实现,我们编写realm时可以继承AuthorizingRealm并重写相关方法完成相关业务的实现。 业务实现:(创建realm类并重写相关方法)
  1. 包名: com.jt.sys.service.realm.ShiroUserRealm
  2. 类名: ShiroUserRealm
  3. 方法: AuthenticationInfo (完成认证信息的获取与封装)
@Service
public class ShiroUserRealm extends AuthorizingRealm {
@Autowired
private SysUserDao sysUserDao;
/**
* 设置凭证匹配器
* @param credentialsMatcher
*/
@Override
public void setCredentialsMatcher(
CredentialsMatcher credentialsMatcher) {
HashedCredentialsMatcher cMatcher=
new HashedCredentialsMatcher();
cMatcher.setHashAlgorithmName("MD5");
super.setCredentialsMatcher(cMatcher);
}
/**
* 执行认证操作时,此方法用户获取用户认证信息
* 说明:此方法由认证管理器调用
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token)throws AuthenticationException {
//1.获取客户端提交的用户信息
UsernamePasswordToken upToken=
(UsernamePasswordToken)token;
String username=upToken.getUsername();
//2.基于用户名从数据库查询用户信息
SysUser user=sysUserDao.findUserByUserName(username);
//3.校验用户信息(用户存在吗)
if(user==null) throw new AuthenticationException("用户不存在");
if(user.getValid()==0) throw new AuthenticationException("此用户被禁用");
//4.对用户信息进行封装
ByteSource credentialsSalt=
ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info=
new SimpleAuthenticationInfo(
user, //principal(用户身份)
user.getPassword(),//hashedCredentials(已经加密的密码)
credentialsSalt, //credentialsSalt(盐)
this.getName());//realm name
return info;//此对象返回给谁了?认证管理器
}
/**
* 执行授权操作时,此方法用于获取用户的权限信息
* 负责完成用户权限领域的信息的获取及封装
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//完成授权操作时需要在此方法完成数据获取与封装
//。。。。。。。。
System.out.println("============doGetAuthorizationInfo=========");
SysUser user = (SysUser) principals.getPrimaryPrincipal();
//基于用户id查找角色id信息
List<Integer> roleIds =  sysUserRoleDao.findRoleIdsByUserId(user.getId());
//基于用户id查找菜单id信息
Integer [] array = {};
List<Integer> menuIds= sysRoleMenuDao.findMenuIdsByRoleId(roleIds.toArray(array));
//基于菜单id查找权限标识信息
List<String> permisssions= sysMenuDao.findPermissions(menuIds.toArray(array));
//对权限标识进行去重和空操作
Set<String> pSet= new HashSet<String>();
for(String permission:permisssions){
if(!StringUtils.isEmpty(permission)){
pSet.add(permission);
}
}
//去重和空(null),空串
System.out.println("pSet="+pSet);
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.setStringPermissions(pSet);
return info;
}
}

 4.Controller类实现

业务描述
  1. 在SysUserService接口及实现类中添加登录方法
  2. 接收用户名及密码参数,并对其进行有效验证
  3. 执行登录认证
代码实现
@RequestMapping("/")
@Controller
public class SysLoginController {
@RequestMapping("doLoginUI")
public String doLoginUI(){
return "login";
}
@RequestMapping("doLogin")
@ResponseBody
public JsonResult doLogin(String username,String password){
//1.获取Subject对象
Subject subject=SecurityUtils.getSubject();
//2.通过Subject提交用户信息,交给shiro框架进行认证操作
//2.1对用户进行封装
UsernamePasswordToken token=
new UsernamePasswordToken(
username,//身份信息
password);//凭证信息
//2.2对用户信息进行身份认证
subject.login(token);
//分析:
//1)token会传给shiro的SecurityManager
//2)SecurityManager将token传递给认证管理器
//3)认证管理器会将token传递给realm
return new JsonResult("login ok");
}
}

10. 客户端实现

5.编写用户登陆页面

在W EB-INF/pages/ 目录下添加登陆页面( login.html)

6.异步登陆操作实现

$(function () {
$(".login-box-body").on("click",".btn",doLogin);
});
function doLogin(){
var params={
username:$("#usernameId").val(),
password:$("#passwordId").val()
}
var url="doLogin.do";
$.post(url,params,function(result){
if(result.state==1){
//跳转到indexUI对应的页面
location.href="doIndexUI.do?t="+Math.random();
}else{
$(".login-box-msg").html(result.message);
}
});
}

5.Shiro 框架授权流程应用实现

11.服务端实现

7.Dao 实现

业务描述:(核心业务是基于用户id获取用户对应的权限 )
  1. 基于用户id查找角色id信息
  2. 基于角色id查找菜单id信息
  3. 基于菜单id查找权限标识信息
业务实现:(在 SysUserRoleDao 中基于用户id查找角色id信息)
  1. 返回值 List <Integer>
  2. 方法名 findRoleIdsByUserId
  3. 参数列表( Integer id
业务实现:(在Sys RoleMenuDao 中基于用户id查找菜单id信息)
  1. 返回值 List <Integer>
  2. 方法名 findMenuIdsByRoleId
  3. 参数列表( Integer[] id
业务实现:(在SysMenuDao中基于菜单id查找权限标识信息)
  1. 返回值 List < String >
  2. 方法名 find Permisssions
  3. 参数列表( Integer[] id
代码实现: SysUserRoleDao 中方法定义
    List<Integer> findRoleIdsByUserId(             Integer id);
S y sRoleMenuDao中方法定义
    List<Integer> findMenuIdsByRoleId(              @Param( "roleIds")Integer...roleIds);
S y sMenuDao中方法定义
    List<String> findPermissions(              @Param( "menuIds")             Integer... menuIds);

8.Mapper实现

业务描述 基于Dao中方法,定义映射元素 代码实现: SysUserRoleMapper中元素定义   
<select id="findRoleIdsByUserId"
resultType="int">
select role_id
from sys_user_roles
where user_id=#{userId}       
</select>
SysRoleMenuMapper中元素定义   
<select id="findMenuIdsByRoleId"
resultType="int">
select menu_id
from sys_role_menus
where role_id in
<foreach collection="roleIds"
open="("
close=")"
separator=","
item="item">
#{item}
</foreach>
</select>
SysMenuMapper中元素定义   
<select id="findPermissions"
resultType="string">
select permission <!-- sys:user:update -->
from sys_menus
where id in
<foreach collection="menuIds"
open="("
close=")"
separator=","
item="item">
#{item}
</foreach>
</select>

 9.Service实现

业务描述 重写对象realm的 doGetAuthorizationInfo 方法,并完成用户权限信息的获取以及封装,最后将信息传递给授权管理器完成授权操作。
@Service
public class ShiroUserRealm extends AuthorizingRealm {
@Autowired
private SysUserRoleDao sysUserRoleDao;
@Autowired
private SysRoleMenuDao sysRoleMenuDao;
@Autowired
private SysMenuDao sysMenuDao;
……
/**执行授权操作时,此方法用于获取用户的权限信息*/
@Override
protected AuthorizationInfo
doGetAuthorizationInfo(
PrincipalCollection principals) {
System.out.println("==AuthorizationInfo==");
//1.获取用户对象(此对应依赖与认证时封装的用户身份)
SysUser user=(SysUser)
principals.getPrimaryPrincipal();
//2.基于用户id查找角色Id
List<Integer> roleIds=
sysUserRoleDao.findRoleIdsByUserId(
user.getId());
//3.基于角色id查找菜单(资源)id
Integer[] array={};
List<Integer> menuIds=
sysRoleMenuDao.findMenuIdsByRoleId(
roleIds.toArray(array));
//4.基于菜单id查找权限标识
List<String> permisssions=
sysMenuDao.findPermissions(
menuIds.toArray(array));
//5.封装权限信息(AuthorizationInfo)
Set<String> pSet=
new HashSet<String>();
for(String permission:permisssions){
if(!StringUtils.isEmpty(permission)){
pSet.add(permission);
}
}//去重和空(null),空串
System.out.println("pSet="+pSet);
SimpleAuthorizationInfo info=
new SimpleAuthorizationInfo();
info.setStringPermissions(pSet);
return info;
}
}

 12.授权检测实现

com / jt / sys /service/ impl /SysUserServiceImpl.java 需要进行授权检测的方法上添加执行此方法需要的权限标识 例如
/**
* 此方法需要权限检测需要添加shiro框架中的
* @RequiresPermissions
* 当底层系统运行时检测到方法上使用了
* @RequiresPermissions 注解就会为业务
* 对象创建一个代理对象,然后在代理对象中调用
* subject.isPermitted("sys:user:valid")
* 方法进行权限检测操作
*com/jt/sys/service/impl/SysUserServiceImpl.java
*/
@RequiresPermissions("sys:user:valid")
@Override
public int validById(Integer id,
Integer valid,
String modifiedUser) {
if(id==null||id<1)
throw new IllegalArgumentException("id值无效");
if(valid==null||(valid!=1&&valid!=0))
throw new IllegalArgumentException("状态值无效");
int rows=sysUserDao.validById(id,valid,modifiedUser);
if(rows==0)
throw new ServiceException("记录可能已经不存在");
return rows;
}

添加启用禁用菜单:

1、用admin用户登陆,点击菜单管理

6.无权限异常处理

/common/controller/GlobalExceptionHandler.java 类中 更改doHandleShiroException方法
/**
* 密码异常操作处理
* @param e
* @return
*/
@ExceptionHandler(ShiroException.class)
@ResponseBody
public JsonResult doHandleShiroException(ShiroException e){
if(e instanceof IncorrectCredentialsException) {
return new JsonResult(0,"密码不正确");
}else if(e instanceof AuthorizationException) {
return new JsonResult(0,"没有权限");
}
return new JsonResult(e);
}

效果:

7、获取登陆用户名使修改信息可见

在com / jt / sys /controller/SysUserController.java类中修改以下方法

@RequestMapping("doSaveObject")
@ResponseBody
public JsonResult doSaveObject( SysUser entity, Integer[]roleIds){
//获取登陆用户信息
SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
entity.setCreatedUser(user.getUsername());
entity.setModifiedUser(user.getUsername());
sysUserService.saveObject(entity, roleIds);
return new JsonResult("save ok");
}
@RequestMapping("doValidById")
@ResponseBody
public JsonResult doValidById(Integer id,Integer valid){
System.out.println(sysUserService.getClass().getName());
//获取登陆用户信息
SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
sysUserService.validById(id,valid,user.getUsername());
return new JsonResult("update ok");
}

6.Shiro框架应用增强

10.Shiro 缓存配置

S tep01: 添加ehcache 依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>

Step02: 添加ehcache 配置文件

在项目的src/main/resources目录下添加ehcache.xml S tep03:  Spring 中配置ehcache 配置文件
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>
将cacheManager添加到securityManager中    
<bean id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<property name="realm" ref="userRealm"></property>
</bean>

 11.Shiro 记住我

课后可自学

7.总结

13.重点和难点分析


7.常见FAQ

 bug-01:角色管理更新出错可能是这里参数问题 java/ com / jt / sys / dao /SysRoleMenuDao.java 类中findMenuIdsByRoleId 方法
/**
* 基于角色查询菜单id的方法
* @param roleId
* @return
*
* 角色管理更新出错可能是这里参数问题
* (@Param("roleIds") Integer... roleIds);
*/
List<Integer> findMenuIdsByRoleId(Integer roleId);

 bug-02:添加用户输入框的数据没有清掉:

在user-edit.html中更改为注释的内容
function doCancel(){
//$("#mainContentId").removeData("rowData");
$("#mainContentId").removeData("id");
$("#mainContentId").load("user/doUserListUI.do");
}

 1                                            齐雷 qilei@tedu

 day07:京淘项目-权限管理子系统-Spring AOP

1.    Spring AOP 概述                                                                                                  2 1.1.    AOP是什么?                                                                                              2 1.2.    AOP要解决什么问题?                                                                                2 1.3.    AOP实际项目应用场景?                                                                             2 1.4.    AOP底层原理实现分析?                                                                             3 1.5.    AOP 相关术语                                                                                               3 2.    Spring AOP 编程基础                                                                                             4 2.6.    AOP 基本步骤                                                                                                4 2.7.    AOP 基于xml实现                                                                                          4 2.7.1.    创建项目添加依赖                                                                                 4 2.7.2.    添加spring配置文件                                                                              5 2.7.3.    创建核心业务类                                                                                    6 2.7.4.    创建日志处理类                                                                                     6 2.7.5.    配置bean对象                                                                                        6 2.7.6.    编写测试类                                                                                            8 2.7.7.    原理分析                                                                                                8 2.8.    AOP 基于注解实现                                                                                          9 2.8.8.    创建maven 项目添加依赖                                                                      9 2.8.9.    创建核心业务类                                                                                    10 2.8.10.    创建时间处理类                                                                                  10 2.8.11.    配置AOP实现                                                                                      11 2.8.12.    编写测试类                                                                                         12 3.    Spring AOP 编程增强                                                                                             13 3.9.    切面表达式增强                                                                                            13 3.9.13.    Bean表达式应用增强                                                                        14 3.9.14.    Within表达式应用增强                                                                      14 3.9.15.    annotation表达式应用增强                                                                14 3.9.16.    Execution表达式应用增强                                                                 14 3.10.    切面通知增强                                                                                              15 3.10.17.    Xml方式通知配置增强                                                                    15 3.10.18.    注解方式通知配置增强                                                                  16 3.11.    切面执行顺序配置增强                                                                              17 3.11.19.    Xml方式配置执行顺序                                                                    17 3.11.20.    注解方式配置执行顺序                                                                  18 4.    总结                                                                                                                       19 4.12.    重点和难点分析                                                                                          19 4.13.    常见FAQ                                                                                                     19 4.14.    作业                                                                                                              19

 1.Spring AOP 概述

1.AOP是什么?

AOP 是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善 实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少模块,一个模块有哪些对象,对象有哪些属性),面向切面中包含一个一个动态过程(在对象运行时动态织入一些功能。) 面向切面应用案例:如图所示

2.AOP要解决什么问题?

实际项目中通常会将系统两大部分:核心关注点和非核心关注点 思考? 编程过程中首先要完成的是什么?核心关注点(核心业务) 非核心关注点如何切入到系统中?硬编码(违背OCP),AOP(推荐) AOP就是要在基于OCP ( 开闭原则 ) 在不改变原有系统核心业务代码的基础上动态添加一些扩展功能。

3.AOP实际项目应用场景?

AOP 通常应用于日志的处理,事务处理,权限处理,缓存处理等等。

 4.AOP底层原理实现分析?

AOP底层基于代理机制实现功能扩展:(了解)
  1. 假如目标对象(被代理对象)实现接口,则底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)
  2. 假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。 

5. AOP 相关术语

切面(aspect): 横切面对象,一般为一个具体类对象(可以借助@Aspect声明) 连接点(joinpoint):程序执行过程中某个特定的点,一般指被拦截到的的方法 切入点(pointcut):对连接点拦截内容的一种定义,一般可以理解为多个连接点的结合. 通知(Advice):在切面的某个特定连接点上执行的动作(扩展功能),例如before,after等 很晦涩难懂,多做例子,做完就会清晰。先可以按白话去理解。

2.Spring AOP 编程基础

6.AOP 基本步骤

step1:创建maven java 项目 step2:添加aop依赖 step3:配置aop 核心(基于xml,基于注解) step4:定义核心业务(核心关注点):推荐先写接口再写实现类 step5:定义扩展业务(非核心关注点) step6:基于配置实现非核心业务的切入 step7:编写测试类进行单元测试

7.AOP 基于xml实现

通过AOP为核心业务类添加日志处理

1.创建项目添加依赖

创建maven java 项目,项目名为CGB-SPRING-DAY04-AOP-01然后在pom文件中添加依赖       
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!--
Spring AOP的实现依托于Aspect框架(AOP框架)
所以要引用1.8.5有问题 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>

 2.添加spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework/schema/beans"
xmlns:p="http://www.springframework/schema/p"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xmlns:context="http://www.springframework/schema/context"
xmlns:tx="http://www.springframework/schema/tx"
xmlns:aop="http://www.springframework/schema/aop"
xmlns:mvc="http://www.springframework/schema/mvc"
xmlns:util="http://www.springframework/schema/util"
xmlns:jpa="http://www.springframework/schema/data/jpa"
xsi:schemaLocation=" 
http://www.springframework/schema/beans
http://www.springframework/schema/beans/spring-beans-4.3.xsd
http://www.springframework/schema/mvc
http://www.springframework/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework/schema/tx
http://www.springframework/schema/tx/spring-tx-4.3.xsd
http://www.springframework/schema/aop
http://www.springframework/schema/aop/spring-aop-4.3.xsd
http://www.springframework/schema/util
http://www.springframework/schema/util/spring-util-4.3.xsd
http://www.springframework/schema/data/jpa
http://www.springframework/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework/schema/context
http://www.springframework/schema/context/spring-context-4.3.xsd">
</beans>

 3.创建核心业务类

创建接口
public interface HelloService {
void sayHello(String msg);
}
创建接口实现类
public class HelloServiceImpl implements HelloService {
public void sayHello(String msg) {
//假设这条语句是我们系统中的核心业务
System.out.println(msg);
}
}

 4.创建日志处理类

后续会将此日志处理类定义为横切面,通过此横切面实现扩展业务
public class LoggingAspect {
public void beforeMethod(){
System.out.println("method start");
}
public void afterMethod(){
System.out.println("method end");
}
}

 5.配置bean对象

<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework/schema/beans"
xmlns:p="http://www.springframework/schema/p"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xmlns:context="http://www.springframework/schema/context"
xmlns:tx="http://www.springframework/schema/tx"
xmlns:aop="http://www.springframework/schema/aop"
xmlns:mvc="http://www.springframework/schema/mvc"
xmlns:util="http://www.springframework/schema/util"
xmlns:jpa="http://www.springframework/schema/data/jpa"
xsi:schemaLocation=" 
http://www.springframework/schema/beans
http://www.springframework/schema/beans/spring-beans-4.3.xsd
http://www.springframework/schema/mvc
http://www.springframework/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework/schema/tx
http://www.springframework/schema/tx/spring-tx-4.3.xsd
http://www.springframework/schema/aop
http://www.springframework/schema/aop/spring-aop-4.3.xsd
http://www.springframework/schema/util
http://www.springframework/schema/util/spring-util-4.3.xsd
http://www.springframework/schema/data/jpa
http://www.springframework/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework/schema/context
http://www.springframework/schema/context/spring-context-4.3.xsd">
<!-- 核心业务对象 -->
<bean id="helloService"
class="spring.beans.HelloServiceImpl"/>
<!-- 配置非核心业务对象(日志处理对象):切面 -->
<bean id="log"
class="spring.aop.LoggingAspect"/>
<!-- AOP配置(切入点,切面) --> 
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut
expression="within(spring.beans.HelloServiceImpl)"
id="logPointCut"/>
<!-- 配置日志处理 -->
<aop:aspect ref="log" >
<aop:before method="beforeMethod"
pointcut-ref="logPointCut"/>
<aop:after  method="afterMethod"
pointcut-ref="logPointCut"/>
</aop:aspect>
</aop:config>
</beans>

 6.编写测试类

public class TestAOP01 {
private ClassPathXmlApplicationContext ctx;
@Before
public void init(){
ctx=new ClassPathXmlApplicationContext("spring-configs.xml");
}
@Test
public void testSayHello(){
HelloService helloService=
ctx.getBean("helloService",HelloService.class);
helloService.sayHello("cgb1712");
}
@After
public void destory(){
ctx.close();
}
}

 7.原理分析

基于 AOP 应用,对其执行过程进行分析,如图所示

 8.AOP 基于注解实现

8.创建maven 项目添加依赖

创建MAVEN JAVA 项目 CGB-SPRING-DAY20-AOP-02 ,然后在POM文件中添加依赖   
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!--
Spring AOP的实现依托于Aspect框架
所以要引用1.8.5有问题
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>

 9.创建核心业务类

定义一个订单接口,此接口中定义多个业务操作。
public interface OrderService {
public void saveOrder();
public void deleteOrder();
}
创建接口核心业务实现类,并使用@Service注解进行修饰

10.创建时间处理类

将此时间处理类作为核心业务增强(一个横切面对象)类,用于输出业务开始执行时间,以及业务结束执行时间。 横切面对象主要由两部分构成:切入点(用于@Pointcut标识),以及功能增强(用通知@Before,@After等进行标识)
@Aspect
@Service
public class TimeAspect {
@Pointcut("bean(orderService)")
public void pointcut(){}
/**增强功能:前置通知(业务方法执行之前执行)*/
@Before("pointcut()")
public void begin(){
System.out.println("start:"+System.nanoTime());
}
/**增强功能:最终通知(业务方法执行最后执行,
*无论业务方法是否执行成功,此功能都要执行)*/
@After("pointcut()")
public void end(){
System.out.println("end:"+System.nanoTime());
}
}
其中: @Aspect 注解用于标识此类为一个AOP横切面对象 @Pointcut 注解用于定义本类中的切入点,本案例中切入点表达式用的是bean表达式,这个表达式以bean开头,bean括号中的内容为一个spring管理的某个bean对象的id。 @Before 用于定义一个前置通知(满足切入点表达式的核心业务方法执行之前要执行的一个操作) @After  用于定义一个后置通知(满足切入点表达式的核心业务方法执行之后要执行的一个操作) 术语增强: 切面:用于封装扩展业务的一个类的对象。 通知:切面扩展业务中的一个操作。

11.配置AOP实现

对于基于注解方式的配置一般有两种方式,一种是直接在xml核心配置文件中进行配置,还有一种在类中基于注解进行配置。例如 基于xml方式配置对注解的应用
<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework/schema/beans"
xmlns:p="http://www.springframework/schema/p"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xmlns:context="http://www.springframework/schema/context"
xmlns:tx="http://www.springframework/schema/tx"
xmlns:aop="http://www.springframework/schema/aop"
xmlns:mvc="http://www.springframework/schema/mvc"
xmlns:util="http://www.springframework/schema/util"
xmlns:jpa="http://www.springframework/schema/data/jpa"
xsi:schemaLocation=" 
http://www.springframework/schema/beans
http://www.springframework/schema/beans/spring-beans-4.3.xsd
http://www.springframework/schema/mvc
http://www.springframework/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework/schema/tx
http://www.springframework/schema/tx/spring-tx-4.3.xsd
http://www.springframework/schema/aop
http://www.springframework/schema/aop/spring-aop-4.3.xsd
http://www.springframework/schema/util
http://www.springframework/schema/util/spring-util-4.3.xsd
http://www.springframework/schema/data/jpa
http://www.springframework/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework/schema/context
http://www.springframework/schema/context/spring-context-4.3.xsd">
<!-- 配置对类组件的扫描 -->
<context:component-scan
base-package="com.spring"/>
<!-- 启用AOP注解(自动为目标对象创建代理对象) -->
<aop:aspectj-autoproxy/>
</beans>
在类中基于注解方式的配置
@ComponentScan("com.spring.beans")
@EnableAspectJAutoProxy
public class AppConfig {
}

 12.编写测试类

基于xml方式注解配置的测试实现
public class TestAOP01 {
public static void main(String[] args) {
//1.初始化容器
ClassPathXmlApplicationContext ctx=
new ClassPathXmlApplicationContext(
"applicationContext.xml");
//2.获取bean对象
OrderService os=(OrderService)
ctx.getBean("orderService",
OrderService.class);
//3.执行业务方法
os.saveOrder();
os.deleteOrder();
//4.释放资源
ctx.close();
}
}
基于类中注解方式配置的测试实现
public class TestAOP02 {
public static void main(String[] args) {
//1.初始化容器对象
AnnotationConfigApplicationContext ctx=
new AnnotationConfigApplicationContext(
AppConfig.class);
//2.获取Bean对象
OrderService orderService=
ctx.getBean("orderService", OrderService.class);
//3.执行业务
orderService.saveOrder();
//orderService.deleteOrder();
//4.释放资源
ctx.close();
}
}
基于类的注解配置,初始化工厂时需要初始化 AnnotationConfigApplicationContext对象。

3.Spring AOP 编程增强

9.切面表达式增强

Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:
指示符 作用
bean 用于匹配指定bean id的的方法执行
within @annotation          用于匹配指定包名下类型内的方法执行 用于匹配指定注解修饰的方法执行
execution 用于进行细粒度方法匹配执行具体业务

13.Bean表达式应用增强

bean应用于类级别,实现粗粒度的控制:
bean (" userServiceImpl ")) 指定一个类
bean (" * Service") 指定所有的后缀为service的类

14.Within表达式应用增强

within应用于类级别,实现粗粒度的切面表达式定义:
within("aop.service .UserServiceImpl ") 指定类,只能指定一个类
within("aop.service .* ") 只包括当前目录下的类
within("aop.service..*") 指定当前目录包含所有子目录中的类

15.annotation表达式应用增强

@anotation 应用于方法级别,实现细粒度的切面表达式定义:
@annotation("common.anno.RequestLog") 表示使用此注解修饰的方法要添加扩展功能

16.Execution表达式应用增强

execution方法级别,细粒度的控制: 语法:execution(返回值类型 包名.类名.方法名(参数列表))
execution(void aop.service.UserServiceImpl.addUser()) 匹配方法
execution(void aop.service.PersonServiceImpl.addUser(String)) 方法参数必须为字符串
execution(* aop.service..*.*(..)) 万能配置

10.切面通知增强

在AOP编程中有五种类型的通知:
  1. 前置通知 ( @Before )
  2. 返回通知 ( @AfterReturning )
  3. 异常通知 ( @AfterThrowing )
  4. 后置通知 ( @After ) : 又称之为最终通知( finally )
  5. 环绕通知 ( @Around ) :课后扩展了解

 17.Xml方式通知配置增强

切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置   
<aop:config>
<aop:pointcut id="pc"expression="execution(*
company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:before method="beforeMethod" pointcut-ref="pc"/>
<aop:after method="afterMethod" pointcut-ref="pc"/>
<aop:after-returning method="returnMethod"
pointcut-ref="pc"/>
<aop:after-throwing method="throwMethod"
pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
切入点及环绕通知的配置(了解)
<aop:config>
<aop:pointcut id="pc"expression="execution(*
company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>

 18.注解方式通知配置增强

切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置
@Aspect
@Service
public class LogAspect {
@Pointcut("bean(orderServiceImpl)")
public void doLog(){}
@Before("doLog()")
public void doBefore(){
System.out.println("log before");
}
@After("doLog()")
public void doAfter(){
System.out.println("log after");
}
/**核心业务正常结束时执行
* 说明:假如有after,先执行after,再执行returning*/
@AfterReturning("doLog()")
public void doAfterReturning(){
System.out.println("log doAfterReturning");
}
/**核心业务出现异常时执行
说明:假如有after,先执行after,再执行Throwing*/
@AfterThrowing("doLog()")
public void doAfterThrowing(){
System.out.println("log doAfterThrowing");
}
}
切入点及环绕通知的配置(课后自己实现)
@Component
@Aspect
public class TxManager {
@Pointcut("execution(company.spring.service..*.*(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable{
try{
System.out.println("事务开始");
Object result = joinPoint.proceed();//执行目标方法
System.out.println("提交事务");
return result;
}catch(Exception e){
System.out.println("回滚事务");
throw e;
}finally{
System.out.println("释放资源");
}
}
}

 11.切面执行顺序配置增强

实际项目中可能会有多个切面,切面之间的执行可能需要一定的顺序

19.Xml方式配置执行顺序

<aop:config>
<aop:pointcut id="pc"
expression="execution(*
company.spring.service..*.*(..))"/>
<aop:aspect ref="loggingAspect" order="1">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
<aop:aspect ref="txManager" order="2">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>

20.注解方式配置执行顺序

注解方式顺序配置需要借助@Order注解
@Order(1)
@Aspect
@Component
public class TxManager {
@Pointcut("execution(* company.spring.service..*.(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable{
System.out.println("事务开始");
Object result = joinPoint.proceed();
System.out.println("事务结束");
return result;
}
}
注解方式顺序配置
@Order(2)
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* company.spring.service..*.(..))")
public void pointCut() {}
@Before("pointCut()")
public void beforeMethod() {
System.out.println("beforeMethod");
}
@Before("pointCut()")
public void afterMethod() {
System.out.println("afterMethod");
}
}

 4.总结

12.重点和难点分析

  1. AOP 是什么,解决了什么问题,实现原理,应用场景
  2. AOP 编程基本步骤及基本实现
  3. AOP 编程中重点掌握基于注解方式配置

13.常见FAQ

  1. 什么是OCP原则(开闭原则):允许扩展 , 不允许修改
  2. 什么是单一职责原则(一个类或接口的职责不要太多)
  3. spring中AOP的有哪些配置方式(xml和注解)
  4. Spring 中AOP 的通知有哪些基本类型
  5. Spring 中AOP是如何为Bean对象创建代理对象的?

  1. 作业

  1. 总结课堂知识点(笔记再详细也是别人的,不是自己的,拿来主义对自己的提高会很慢,无论是否认同,可以试试,看看最后效果)
  2. 完成课堂AOP基本案例
  3. 了解代理模式及应用场景,实现方式(最好自己尝试实现)
  4. 尝试自己基于JDK为某个类创建一个代理对象.(Proxy.newProxyInstance(...))
  5. 尝试自己基于CGLIB为某个类创建一个代理对象(依赖于 cglib 库)

1                                                             齐雷

            京淘项目

  Spring 事务管理 1.    Spring 事务管理                                                                                                1 1.1.    Spring 事务概述                                                                                       1 1.2.    Spring事务案例分析                                                                                  2 2.    Spring 声明式事务处理                                                                                       3 2.3.    基于注解方式实现                                                                                      3 2.4.    基于xml方式实现                                                                                        4 3.    Spring事务增强                                                                                                    5 3.5.    Spring 事务的传播特性                                                                                5 3.6.    Spring 事务的隔离级别                                                                                6 4.    总结                                                                                                                       8 4.7.    重点和难点分析                                                                                            8 4.8.    常见FAQ                                                                                                       8

1.Spring 事务管理

1.Spring 事务概述

事务是一个不可分割的逻辑工作单元,具备ACID特性,实际工作中可借助Spring进行事务管理。 事务四大特性:ACID
  1. 原型子型(一个事务中的多个操作要么都成功要么都失败)
  2. 一致性(例如存钱操作,存之前和存之前的钱数应该是一致的)
  3. 隔离性(事务与事务应该是相互隔离的)
  4. 持久性(事务一旦提交,数据要持久保存)
Spring提供了两种事务管理方式, 编程式事务和声明式事务。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。 Spring 中声明式事务处理有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于@Transactional 注解的方式。 本讲重点讲解实际项目中最常用的声明式事务管理,以简化事务的编码操作。

2.Spring事务案例分析

例如现有两个订单操作,需要更新库存。 当库存充足时两个事务都可以成功,当库存不够时有的事务就要回滚。 说明:Spring声明式事务管理底层基于AOP实现

 2.Spring 声明式事务处理

3.基于注解方式实现

S tep1:在spring配置文件中启用事务注解
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--设置注解驱动的事务管理  -->
<tx:annotation-driven transaction-manager="txManager"/>
Step2:在类或方法中使用@Transaction注解应用事务。
  • value    当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。
  • propagation   事务的传播行为,默认值为 REQUIRED。
  • isolation  事务的隔离度,默认值采用 DEFAULT。
  • timeout    事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
  • read-only  指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
  • rollback-for  用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
  • no-rollback- for  抛出 no-rollback-for 指定的异常类型,不回滚事务。
说明: @Transactional 注解可以用在方法上也可以添加到类级别上。当把@Transactional 注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息。见清单 2,EmployeeService 的所有方法都支持事务并且是只读。当类级别配置了@Transactional,方法级别也配置了@Transactional,应用程序会以方法级别的事务属性信息来管理事务,换言之,方法级别的事务属性信息会覆盖类级别的相关配置信息。

4.基于xml方式实现

在配置文件中通过xml配置方式实现声明式事务管理。 配置事务管理器
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
配置事务处理方式
< tx:advice id= " txAdvice "  transaction-manager= "txManager" > < tx:attributes > < tx:method name= "*" propagation= "REQUIRED" isolation= "READ_COMMITTED" timeout= "-1" read-only= "false" rollback-for= "java.lang.Throwable" no-rollback-for= ”NoTransactionException" /> </ tx:attributes > </ tx:advice >
< aop : config > < aop : pointcut id= " operation " expression= " execution(* beans.service..*.*(..)) " /> < aop : advisor advice-ref= " txAdvice " pointcut= " operation " /> </ aop : config >
课堂练习: Step 01:定义事务管理器    
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
定义事务策略
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--定义查询方法都是只读的 -->
<tx:method name="query*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="get*" read-only="true" /
<!-- 主库执行操作,事务传播行为定义为默认行为 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<!--其他方法使用默认事务策略 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 定义切面,所有的service的所有方法 --
<aop:pointcut id="txPointcut"
expression="execution(* com.jt.sys.service..*.*(..))" />
<!-- 应用事务策略到Service切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

4.基于注解方式实现

1、在 com / jt /common/ config /AppRootConfig.java类添加来配置事务管理器
@Bean("txManager")
public DataSourceTransactionManager newDataSourceTransactionManager(
@Autowired DataSource dataSource) {
DataSourceTransactionManager tManager=  new DataSourceTransactionManager();
tManager.setDataSource(dataSource);
return tManager;
}
2、设置注解驱动的事务管理 3、第三步 4、效果: 5、再查看数据库数据 并没有插入数据说明事物操作ok

 3.Spring事务增强

5.Spring 事务的传播特性

事务传播特性:事务方法之间相互调用时,事务的传播方式. 重点掌握 Propagation.REQUIRED @Transactional(propagation=Propagation.REQUIRED)  如果没有事务创建新事务, 如果当前有事务参与当前事务 @Transactional(propagation=Propagation.REQUIRES_NEW) 必须是新事务, 如果有当前事务, 挂起当前事务并且开启新事务. 课后了解 : @Transactional(propagation=Propagation.MANDATORY) 必须有事务, 如果当前没有事务就抛异常 @Transactional(propagation=Propagation.NEVER) 绝对不能有事务, 如果在事务中调用则抛出异常 @Transactional(propagation=Propagation.NESTED) 必须被嵌套到其他事务中 @Transactional(propagation=Propagation.NOT_SUPPORTED) 不支持事务 @Transactional(propagation=Propagation.SUPPORTS) 支持事务, 如果没有事务也不会创建新事务

6.Spring 事务的隔离级别

思考:多个事务并发执行时可能会导致什么问题?(脏读,不可重复读,幻读)

当多个事务并发执行时,可通过设置事务的隔离级别保证事务的完整性,一致性。

事务的隔离级别从低到高有如下几种方式:

1)READ_UNCOMMITTED (此级别可能会出现脏读)

2)READ_COMMITTED(此级别可能会出现不可重复读)

3)REPEATABLE_READ(此级别可能会出现幻读)

4)SERIALIZABLE(多事务串行执行)

说明:spring中一般采用 @Transactional(isolation=Isolation.READ_COMMITTED) 方式声明级别, 这种方式是并发性能和安全性折中的选择. 是大多数软件项目采用的隔离级别.

回顾MySQL中隔离级别:

查看InnoDB存储引擎 系统级的隔离级别 和 会话级的隔离级别

更改会话级的隔离级别

更改系统级的隔离级别

思考:

1)MySQL 中如何查看当前系统默认隔离级别?

show variables like '%storage_engine%';

2)MySQL 中如何设置事务隔离级别?

set session transaction isolation level 'reapable read



作者:Darren

QQ:603026148

以上内容归Darren所有,如果有什么错误或者不足的地方请联系我,希望我们共同进步。

更多推荐

py-18-PROJECT4(下篇)

本文发布于:2023-06-14 09:07:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1459538.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:下篇   py

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!