续 Spring验证框架

上次课我们已经将实体类验证规则编写在属性上了

下面要启动SpringValidation框架的验证了

控制器中启动验证功能

我们的注册功能的控制器是SystemController

打开这个类,找到注册方法

修改代码如下

@PostMapping("/register")
public String register(// 我们可以通过添加@Validated注解启动SpringValidation的验证// 一旦在控制器方法参数前添加@Validated,表示控制器方法运行前// 先由SpringValidation框架按照RegisterVO类中编写的验证规则进行验证@Validated RegisterVO registerVO,// 下面的参数就是SpringValidation框架验证的结果对象// 对registerVO对象属性的验证信息会自动保存到result对象中BindingResult result
) {// 使用@Slf4j提供的log对象,将registerVO信息输出到日志log.debug("接收到表单信息:{}", registerVO);// 判断result对象中有没有验证失败的信息if(result.hasErrors()){// 进入if表示registerVO对象中有属性没有通过验证// 下面来获取这个信息(信息就是验证未通过的message的值)String msg=result.getFieldError().getDefaultMessage();return msg;}try {userService.registerStudent(registerVO);return "ok";} catch (ServiceException e) {// 将错误信息输出到日志的error级别log.error("注册失败", e);// 控制器返回的字符串就是业务逻辑层中的错误信息文本return e.getMessage();}
}

下面可以进行测试

但是测试之前,要先删除html页面中的html5的表单验证代码

建议在测试成功之后,回复删除掉的html5验证代码

开发显示首页标签

首页标签效果

显示首页标签的开发流程

显示首页标签列表的开发流程

1.学生访问首页,在页面加载完毕时利用Vue调用axios请求

2.axios请求到TagController控制器中的方法

3.控制器方法调用TagService业务逻辑层中获得所有标签的方法

4.TagService会使用Mapper连接数据库查询所有标签并返回给控制器

5.控制器得到所有标签后显示在页面上

之前完成的注册时有表单提交的

下面要完成的查询所有标签的功能是没有表单的,普通的get请求

实际开发中,程序员一般会从底层编写代码

数据访问层->业务逻辑层->控制层

本次功能查询的所有标签的数据访问层方法,已经有MybatisPlus框架提供了,无需我们编写,所以直接从业务逻辑层开始

开发业务逻辑层

开发业务逻辑层先编写接口

查询所有表单明显是ITagService接口,添加方法如下

public interface ITagService extends IService<Tag> {// 定义全查所有标签的业务逻辑层方法List<Tag> getTags();}

TagServiceImpl实现类编写代码如下

@Service
public class TagServiceImpl extends ServiceImpl<TagMapper, Tag> implements ITagService {@Autowiredprivate TagMapper tagMapper;@Overridepublic List<Tag> getTags() {// 调用查询所有标签的方法List<Tag> tags=tagMapper.selectList(null);// 千万别忘了返回tagsreturn tags;}
}

编写控制层代码

TagController类编写调用业务逻辑层方法,获得所有标签

并将所有标签返回给前端页面

@RestController
@RequestMapping("/v1/tags")
public class TagController {// 添加业务逻辑层的依赖注入@Autowiredprivate ITagService tagService;// @GetMapping("")写法的含义就是只使用类上定义的路径作为当前控制方法的路径// localhost:8080/v1/tags@GetMapping("")public List<Tag> tags(){List<Tag> tags=tagService.getTags();return tags;}
}

重启portal项目

我们想测试一下这个控制器方法是否能够正确运行,返回所有标签

我们可以打开浏览器,在浏览器地址栏直接输入localhost:8080/v1/tags

如果看到所有标签的json格式返回值表示一切正确

我们将这样的操作称之为"浏览器同步测试"

特别适合测试支持Get请求的控制器方法,今后我们可以多使用这种方法测试我们的java代码,尤其是报错的时候,可以定位错误出现的位置

显示结果如下

编写Vue绑定和Js代码

这次我们的页面换为了index_student.html

要想让这个页面显示出所有标签

我们要按下面步骤依次完成

1.添加axios的引用

  <!--引入CDN服务器的框架文件--><script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
</head>

2.在页面尾部添加js引用

<script src="js/utils.js"></script>
<script src="js/tags_nav.js"></script>
</body>
</html>

3.index_student.html的163行附近进行Vue的绑定操作

<!--引入标签的导航栏-->
<div class="container-fluid"  th:fragment="tags_nav" ><div class="nav font-weight-light" id="tagsApp"><a href="tag/tag_question.html" class="nav-item nav-link text-info"><small>全部</small></a><a href="tag/tag_question.html"class="nav-item nav-link text-info"v-for="tag in tags"><small v-text="tag.name">Java基础</small></a></div>
</div>

重启服务测试

发现如果不登录直接访问学生首页,有大面积空白,因为学生首页放行,但是/v1/tags没有放行导致的

必须在登录页登录后,再访问学生首页才正常

要解决这个问题我们采用取消学生首页放行的办法

原因有二

1:显示所有标签的控制器方法没有放行

2:更重要的是今后我们实现显示当前登录用户所有问题时,必须知道当前登录用户是谁

打开SecurityConfig类

注释或删除学生首页放行的行

.antMatchers(// 指定路径//"/index_student.html","/css/*","/js/*","/img/**","/bower_components/**","/login.html","/register.html",  // 放行注册页"/register"        // 放行注册控制器路径
)

再次重启服务,访问学生首页就必须先登录了

实现标签的缓存

什么是缓存

缓存指计算机(服务器)内存保存的数据,一般用于支持快速访问

简单来说,就是将一些经常被使用的数据,保存在内存中,以提高访问效率和速度

为什么需要缓存

因为如果一个数据保存在数据库中,经常被使用时反复连接数据库,效率低

如果将这个数据保存在内存中,在使用时直接从内存获取,提高访问效率,所以使用缓存来保存这些经常被访问的数据

缓存的使用情景

什么时候使用缓存

要结合我们计算机内存的优缺点

内存

优点:快

缺点:

  • 相对硬盘容量小,不能将硬盘所有数据都保存,一旦内存空间不足,运行明显变慢
  • 内存中的数据都是"易失"的,一旦断电,信息就没了

综合内存优缺点

行业中,一般满足下面3中情况时,这样的数据建议保存在缓存中

  • 数据量不能太大
  • 数据经常被访问或使用
  • 数据库缓存的数据不应该被频繁修改,或对修改并不敏感

实现缓存全部标签列表

经过分析,我们的标签列表tags就适合保存在缓存中

以提高访问效率

下面我们就修改TagServiceImpl类,实现标签列表的缓存

// 声明缓存对象
private List<Tag> tags=new CopyOnWriteArrayList<>();
// tags属性用于充当保存所有标签的缓存对象
// 因为TagServiceImpl默认是单例作用域的,所以tags属性也只有一份
// 在今后任何方法的访问中,不会出现新的对象,重复占用内存
// CopyOnWriteArrayList是jdk1.8开始支持的线程安全的集合对象
@Override
public List<Tag> getTags() {// 要获得所有标签// 先判断缓存对象是不是没有元素//     3if(tags.isEmpty()){synchronized (tags) {//     2tags.clear();List<Tag> list = tagMapper.selectList(null);tags.addAll(list);System.out.println("tags加载完成");}//  1}// 千万别忘了返回tagsreturn tags;
}

开发显示学生问题列表

显示学生问题列表业务流程

怎么去查询一个用户的所有问题呢?

我们可以先编写一个sql语句

select * from question
where user_id=11 and delete_status=0

sql语句比较简单,而且只有在学生首页时需要查询

所以可以使用QueryWrapper来完成

所以直接开发业务逻辑层

开发业务逻辑层

先写接口方法

IQuestionService添加方法如下

public interface IQuestionService extends IService<Question> {// 根据登录用户的用户名查询问题列表List<Question> getMyQuestions(String username);
}

转到QuestionServiceImpl类编写实现代码如下

@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements IQuestionService {@Autowiredprivate QuestionMapper questionMapper;@Autowiredprivate UserMapper userMapper;@Overridepublic List<Question> getMyQuestions(String username) {// 先根据用户名查询用户信息(用户对象)User user=userMapper.findUserByUsername(username);// 再使用QueryWrapper完成该用户的问题列表的查询QueryWrapper<Question> query=new QueryWrapper<>();query.eq("user_id",user.getId());query.eq("delete_status",0);query.orderByDesc("createtime");// 执行查询List<Question> list=questionMapper.selectList(query);// 别忘了返回listreturn list;}
}

编写控制层代码

QuestionController类添加方法

@RestController
@RequestMapping("/v1/questions")
public class QuestionController {@Autowiredprivate IQuestionService questionService;// localhost:8080/v1/questions/my@GetMapping("/my")public List<Question> my(//@AuthenticationPrincipal注解效果// 从Spring-Security框架获得当前登录用户的UserDetails对象// 赋值给注解之后的参数@AuthenticationPrincipal UserDetails user){List<Question> questions=questionService.getMyQuestions(user.getUsername());// 返回业务逻辑层查询出的所有问题列表return questions;}
}

重启服务,发送同步请求

localhost:8080/v1/questions/my

一定要求我们登录才能访问,注意,登录的必须是在数据库中有问题的用户

例如st2,xiaom,不要登录自己注册的用户,因为没有问题数据

Vue绑定和js代码

要想将我们查询出的问题列表信息显示在页面上,还需要编写对应的VUE绑定

首先在页面尾部添加引用

<script src="js/utils.js"></script>
<script src="js/tags_nav.js"></script>
<!--  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓  -->
<script src="js/index.js"></script>
</body>
</html>

然后开始编写html代码的Vue绑定

index_student.html的182行附近

<div id="questionsApp">
<div class="row" style="display: none"><div class="alert alert-warning w-100" role="alert">抱歉您还没有提问内容, <a href="question/create.html" class="alert-link">您可以点击此处提问</a>,或者点击标签查看其它问答</div>
</div><div class="media bg-white m-2 p-3"v-for="question in questions"><!--  ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑  --><div class="media-body w-50"><div class="row"><div class="col-md-12 col-lg-2"><span class="badge badge-pill badge-warning" style="display: none">未回复</span><span class="badge badge-pill badge-info" style="display: none">已回复</span><span class="badge badge-pill badge-success">已解决</span></div><div class="col-md-12 col-lg-10"><h5 class="mt-0 mb-1 text-truncate"><a class="text-dark"href="question/detail.html"v-text="question.title"><!--  ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑  -->eclipse 如何导入项目?</a></h5></div></div><div class="font-weight-light text-truncate text-wrap text-justify mb-2" style="height: 70px;"><p v-html="question.content"><!--  ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑  -->eclipse 如何导入项目?</p></div><div class="row"><div class="col-12 mt-1 text-info"><i class="fa fa-tags" aria-hidden="true"></i><a class="text-info badge badge-pill bg-light" href="tag/tag_question.html"><small >Java基础 &nbsp;</small></a></div></div><div class="row"><div class="col-12 text-right"><div class="list-inline mb-1 "><small class="list-inline-item"v-text="question.userNickName">风继续吹</small><!--  ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑  --><small class="list-inline-item"><span v-text="question.pageViews">12</span><!--  ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑  -->浏览</small><small class="list-inline-item" >13分钟前</small></div></div></div></div><!-- / class="media-body"--><img src="img/tags/example0.jpg"  class="ml-3 border img-fluid rounded" alt="" width="208" height="116">
</div>

因为js代码都是写好的

所以直接重启服务,就能显示所有问题列表内容了!

显示持续时间

所谓持续时间,就是问题发布时间距离现在的时间差

页面上固定值"13分钟前"

我们要将这个值修改为真实的时间

我们设计将持续时间的显示分为4个分段

  • 时间差不足1分钟,显示"刚刚"
  • 时间差不足1小时,显示"XX分钟前"
  • 时间差在1天以内,显示"XX小时前"
  • 时间差在1天以上,显示"XX天前"

实现的逻辑就是用当前时间减去问题发布的时间

在判断这个时间差的范围,代码已经在index.js文件中编写完毕

index_student.html页面的226行附近,添加

<small class="list-inline-item"v-text="question.duration">13分钟前</small>

重启服务就能显示持续时间了

显示当前问题关联的标签

上面的图片就是一个问题关联了多个标签的显示效果

先明确数据库中标签和问题的关系

因为question和tag是多对多的关系,一旦需要根据问题id查询该问题对应的标签就需要一个3表的关联查询

这个3表关联查询,性能差,程序员不愿意写,带来各种问题

所以我们希望避免关联查询

实际开发中,我们可以将经常需要关联查询的内容直接保存在当前数据表中,在需要时直接查询当前表,避免关联查询

但是我们要知道,这样做会出现数据库表的数据冗余,占用更多数据库空间同时增加数据库维护难度,开发中需要斟酌利弊才能确定的

为了实现一个问题对象中包含多个标签的表示

我们修改Question实体类,添加一个标签集合的属性

/*** 当前问题包含的所有标签的集合*/
// 声明当前属性不对应数据库表中的任何列
@TableField(exist = false)
private List<Tag> tags;

下面我们就要完成将tagNames属性中例如

"Java基础,Java SE,面试题"的字符串

转换为一个List<Tag>类型的对象,其中包含

  • Java基础标签对象
  • Java SE标签对象
  • 面试题标签对象

具体思路在文档末尾

按照上面思路,我们需要先到TagService业务逻辑层中,声明包含所有标签的Map类型缓存对象

先声明接口中的方法

ITagService添加方法如下

// 定义返回包含所有标签对象Map的方法
Map<String,Tag> getTagMap();

TagServiceImpl类实现代码如下

添加tagMap缓存对象

@Service
public class TagServiceImpl extends ServiceImpl<TagMapper, Tag> implements ITagService {@Autowiredprivate TagMapper tagMapper;// 声明缓存对象private List<Tag> tags=new CopyOnWriteArrayList<>();// tags属性用于充当保存所有标签的缓存对象// 因为TagServiceImpl默认是单例作用域的,所以tags属性也只有一份// 在今后任何方法的访问中,不会出现新的对象,重复占用内存// CopyOnWriteArrayList是jdk1.8开始支持的线程安全的集合对象private Map<String,Tag> tagMap=new ConcurrentHashMap<>();// ConcurrentHashMap是一个线程安全的Map集合类型对象,从jdk1.8开始@Overridepublic List<Tag> getTags() {// 要获得所有标签// 先判断缓存对象是不是没有元素//     3if(tags.isEmpty()){synchronized (tags) {//     2tags.clear();tagMap.clear();List<Tag> list = tagMapper.selectList(null);tags.addAll(list);// 遍历list对tagMap赋值for(Tag t:list){tagMap.put(t.getName(),t);}System.out.println("tags加载完成");}//  1}// 千万别忘了返回tagsreturn tags;}@Overridepublic Map<String, Tag> getTagMap() {// 判断tagMap是不是emptyif(tagMap.isEmpty()){// 如果tagMap是empty,证明getTags方法也没有运行过// 调用getTags方法为tagMap赋值即可getTags();}// 千万别忘了返回return tagMap;}
}

有了上面提供的缓存的Map对象

我们就可以在Question的业务逻辑层中进行转了

在QuestionServiceImpl类中编写一个转换方法

代码如下

// 编写TagNames转换为List<Tag>的方法
// 需要获得ITagService业务逻辑层中的缓存Map
@Autowired
private ITagService tagService;
private List<Tag> tagNamesToTags(String tagNames){//tagNames:"Java基础,Java SE,面试题"String[] names=tagNames.split(",");// names:{"Java基础","Java SE","面试题"}Map<String,Tag> tagMap=tagService.getTagMap();// 循环遍历之前声明一个用于接收返回值的ListList<Tag> tags=new ArrayList<>();// 遍历当前的names数组for(String name:names){// 根据标签名称获得标签对象Tag t=tagMap.get(name);// 将获得的标签对象赋值到tags中tags.add(t);}// 返回tagsreturn tags;}

英文

Principal:当事人

tagNames转换为List<Tag>的思路

以106号问题为例

tagNames的值为:“Java基础,Java SE,面试题”

我们先利用String类的方法split将这个字符串拆分为String的数组

String[] names=tagNames.split(",");

names:{“Java基础”,“Java SE”,“面试题”}

下面最重要的环节就是怎么将"Java基础"这样的标签名称转换为对应的标签对象

我们知道通过一个名称寻找对应对象最快的数据结构是Map

所以为了进一步提高从内存中通过标签名称获得标签对象的效率

我们先在TagServiceImpl类中再定义一个包含所有标签对象的Map集合

以便我们在这个转换过程中通过标签名称获得标签对象

假设这个缓存的Map对象为tagMap

获得对应标签对象的代码为

Tag t=tagMap.get(names[i]);

最后将这个t对象保存在一个List<Tag>中即可完成转换

list.add(t);

假设缓存对象名称为tags

for(Tag t:tags){if(t.getName.equals("Java基础")){// 进这个if的t对象就是"Java基础"对应的标签对象}
}

ProjectDay04相关推荐

最新文章

  1. php分享二十八:mysql运行中的问题排查
  2. 一件有趣的事:用Python爬了自己的微信朋友圈
  3. 【Flutter】Flutter 拍照示例 ( 浮动按钮及点击事件 | 底部显示按钮组件 | 手势检测器组件 | 拍照并获取当前拍摄照片 | 从相册中选择图片 )
  4. 入门Java开发,这三大知识体系你要知道
  5. boost::geometry::segment_intersection_points用法的测试程序
  6. 实例讲解C语言的位运算
  7. 宝塔 没有找到站点_宝塔面板正确开启TLS 1.3并删除TLS 1.1
  8. 天池竞赛-津南数字制造算法挑战赛【赛场二】解决方案分享
  9. x 6什么意思python_Python基础_6
  10. Python OOP:继承、单继承、多继承、__mro__、子类重写父类同名属性和方法、子类调用父类同名属性和方法、多层继承、super()、私有(实例)属性和方法、获取修改私有属性值、私有类属性
  11. Eclipse自定义启动画面和状态栏图标以及各种小图标的含义
  12. atheros蓝牙设备驱动 小米_双十一值得买的蓝牙耳机,真无线蓝牙耳机音质排行榜...
  13. 啊哈算法——第一章第二节:冒泡排序
  14. RxSwift系列—RxSwift核心逻辑
  15. ShowDown.js MD 转HTML 时的问题
  16. 【2020年高被引学者】 朱松纯 北京大学
  17. 浅谈嵌入式MCU软件开发之S32K1xx系列MCU启动过程及重映射代码到RAM中运行方法详解
  18. 基于单片机的多功能智能指纹_手机_门禁卡_按键密码锁系统设计
  19. sap砍刀-做了sap半年多了,但是一直没有遇到多少问题,今天在网上看到这篇文章,于是copy过来了(对sap的学习者很有用)...
  20. 【项目分享~写给应届生的一篇文章】基于Web电影院购票系统 ~~ 选座模块

热门文章

  1. python torch学习(一)
  2. LeetCode1446. 连续字符
  3. 2022-2028年中国桌面云产业发展动态及市场需求预测报告
  4. Linux:for语句的基本用法举例
  5. (4.6.26)Android特殊系统的校验方式
  6. 微信体现计算机网络功能,教你微信提现如何免手续费
  7. 【并行计算-CUDA开发】GPU 的硬体架构 关于存储体的介绍比较好bank conflicts
  8. 基于Xilinx artix 7的FPGA高级应用(二):千兆以太网通信(原理篇)
  9. 刚刚,2022中科院分区表发布(附下载)
  10. 自制NixieDisplay辉光数码管显示时钟