赞
踩
详情:https://www.cnblogs.com/swzx-1213/p/12781836.html
1.导入依赖
<!--日志依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<!--切面依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
2.设置切面
@org.aspectj.lang.annotation.Aspect @Component public class Aspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); //定义切入点 @Pointcut("execution(* com.yan.yong.controller.*.*(..))") public void log(){} @Before("log()") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); logger.info("--------before------------"); // 记录下请求内容 // logger.info("URL : " + request.getRequestURL().toString()); // logger.info("HTTP_METHOD : " + request.getMethod()); // logger.info("IP : " + request.getRemoteAddr()); logger.info("URL: {},HTTP_METHOD : {},IP : {}",request.getRequestURL().toString(),request.getMethod(),request.getRemoteAddr()); Enumeration<String> enu = request.getParameterNames(); while (enu.hasMoreElements()) { String name = (String) enu.nextElement(); logger.info("name:{},value:{}", name, request.getParameter(name)); } } //获取返回值 @AfterReturning(returning = "result",pointcut = "log()") public void lkj(Object result){ logger.info("result为:{}",result); } @After("log()") public void after(){ logger.info("------------after---------"); } }
1.使用到的注解:
@Component 将给全局异常类注入到IOC容器中
@ControllerAdvice 全局异常处理的注解
@ExceptionHandler统一处理某一类异常
@Component @ControllerAdvice public class ControllerExceptionHandler { private Logger logger = LoggerFactory.getLogger(this.getClass()); @ExceptionHandler(Exception.class) public ModelAndView handler(HttpServletRequest request,Exception e)throws Exception { logger.error("URL : {},exceptinMessage :{}",request.getRequestURL(),e.getMessage()); ModelAndView mv = new ModelAndView(); mv.addObject("exception",e);//将异常信息返回到前前端页面 mv.setViewName("error/error"); return mv; } }
前端接收(可以通过查看源码进入查看异常信息)
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.ory"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> error <!--接收异常信息--> <div> <div th:utext="'<!--'" th:remove="tag"></div> <div th:utext="'Failed Request URL : ' + ${url}" th:remove="tag"></div> <div th:utext="'Exception message : ' + ${exception.message}" th:remove="tag"></div> <ul th:remove="tag"> <li th:each="st : ${exception.stackTrace}" th:remove="tag"><span th:utext="${st}" th:remove="tag"></span></li> </ul> <div th:utext="'-->'" th:remove="tag"></div> </div> </body> </html>
在mvc架构的项目中,服务端和客户端之间可以直接进行html的映射,同时也可以直接返回异常信息给客户端。随着技术不断更新迭代,为了降低前后端的耦合性,前后端分离项目时代开启,这时在发一个请求给后端,如果出现异常信息就需要我们在后端使用springboot提供的全局的异常处理,获取异常信息之后,统一将异常信息以json格式发送给前端。
1.统一返回信息类的构造思路
2.404异常的判断
3.自定义异常
4.手动配置spingboot捕捉404
package com.yan.yong.utils; import com.yan.yong.handler.DefindException; import java.util.HashMap; import java.util.Map; public class Msg { private Integer code; private String message; Map<String,Object> map = new HashMap<>(); //成功正常返回json数据 public static Msg success(){ Msg msg = new Msg(); msg.setCode(200); msg.setMessage("操作成功"); return msg; } //失败正常返回json数据 public static Msg fail(){ Msg msg = new Msg(); msg.setCode(200); msg.setMessage("操作成功"); return msg; } //返回拿到的数据 public Msg sendData(String detail,Object object){ Map<String,Object> map = this.map; map.put(detail,object); return this; } //返回自定义异常的提示信息 public static Msg defindException(DefindException e){ Msg msg = new Msg(); msg.setCode(e.getCode()); msg.setMessage(e.getMessage()); return msg; } //返回默认异常的提示信息 public static Msg defaultException(Integer code,Exception e){ Msg msg = new Msg(); msg.setCode(code); msg.setMessage(e.getMessage()); return msg; } public Map<String, Object> getMap() { return map; } public void setMap(Map<String, Object> map) { this.map = map; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
package com.yan.yong.handler; public class DefindException extends RuntimeException{ private Integer code; private String message; public DefindException(Integer code,String message){ this.code = code; this.message = message; } public Integer getCode() { return code; } @Override public String getMessage() { return message; } }
@Component @ControllerAdvice public class ControllerExceptionHandler { //控制台打印日志 private Logger logger = LoggerFactory.getLogger(this.getClass()); //自动收集自定义异常 @ExceptionHandler(DefindException.class) @ResponseBody public Msg defind(HttpServletRequest request, DefindException e){ logger.info("URL : {}; errorMessage : {}",request.getRequestURL(),e.getMessage()); return Msg.defindException(e); } //自动收集默认异常 @ExceptionHandler(Exception.class) @ResponseBody public Msg defind(HttpServletRequest request,Exception e){ logger.info("URL : {}; errorMessage : {}",request.getRequestURL(),e.getMessage()); if (e instanceof NoHandlerFoundException){ return Msg.defaultException(404,e); }else { return Msg.defaultException(500,e); } } }
@org.springframework.stereotype.Controller public class Controller { @Autowired private Service service; //测试500错误 @GetMapping("/") public String index(){ int i = 5/0; return "index"; } //测试自定义异常 @GetMapping("/str") public String index1(){ String str = null; if (str==null){ throw new DefindException(500,"str为空了"); } return "index"; } //测试正常情况的数据返回 @GetMapping("/data") @ResponseBody public Msg index2(){ int a[][]= new int[2][3]; for (int i=0;i<2;i++){ for (int j=0;j<3;j++){ a[i][j]=(int)(Math.random()*10+1); } } return Msg.success().sendData("data",a); }
由于springboot没有拦截404异常的自动配置,需要手动配置
spring.mvc.throw-exception-if-no-handler-found=true
spring.web.resources.add-mappings=false
1.测试500运行错误
2.测试自定义异常
3.测试正常数据的返回
4.测试404异常
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
package com.yan.yong.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 public class ApiController { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) // 指定构建api文档的详细信息的方法:apiInfo() .apiInfo(apiInfo()) .select() // 指定要生成api接口的包路径 .apis(RequestHandlerSelectors.basePackage("com.yan.yong.controller")) //使用了 @ApiOperation 注解的方法生成api接口文档 //.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.any()) //可以根据url路径设置哪些请求加入文档,忽略哪些请求 .build(); } /** * 设置api文档的详细信息 */ private ApiInfo apiInfo() { return new ApiInfoBuilder() // 标题 .title("Spring Boot集成Swagger2") // 接口描述 .description("swagger核试验场地") .version("1.0") // 构建 .build(); } //出现404访问不到的情况 public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); } }

//测试正常情况的数据返回
@GetMapping("/data/{id}")
@ApiOperation("获取学生所有信息")//对接口的描述
@ApiImplicitParam(name = "id",value = "id",required = true, paramType = "path",dataType = "int")//对请求参数的描述
public Msg index2(@PathVariable("id")Integer id){
System.out.println("id的值为"+id);
int a[][]= new int[2][3];
for (int i=0;i<2;i++){
for (int j=0;j<3;j++){
a[i][j]=(int)(Math.random()*10+1);
}
}
return Msg.success().sendData("data",a);
}
@PostMapping("/student")
@ApiOperation("传入学生信息")//对接口的描述
public Msg addStudent(@RequestBody Student student){
System.out.println(student);
return Msg.success().sendData("studentMessag",student);
}
相关实体类
@Data
@ToString
@ApiModel//声明api实体模型
public class Student {
@ApiModelProperty("学生姓名")
String name;
@ApiModelProperty("学生年龄")
Integer age;
@ApiModelProperty("所在学校")
String school;
}


返回的数据

根据blog_id和parentId==null来查找父级评论
<select id="selectParentComment" resultMap="BaseResultMap">
select *
from t_comment
where parent_comment_id = -1 and blog_id = #{blogId}
</select>
根据parentId查找对应的子集评论
<!--根据父级评论的id寻找子级评论及该子级评论的父级昵称-->
<select id="selectCommentByParent" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select tc.id id, tc.avatar avatar, tc.content content, tc.create_time create_time, tc.email email, tc.nickname nickname,
tc.blog_id blog_id, tc.parent_comment_id parent_comment_id,tp.nickname parentNickname,tc.adminComment
from t_comment tc
left join t_comment tp
on tc.parent_comment_id = tp.id
where tc.parent_comment_id = #{id}
</select>
@Service public class CommentServiceImpl { @Autowired private CommentMapper commentMapper; public int insertComment(Comment comment){//添加评论 return commentMapper.insertSelective(comment); } public List<Comment> listCommentByBlogId(Integer blogId) {//获取所有评论,然后将对评论进行分类,最后返回给controller List<Comment> comments1 = commentMapper.selectParentComment(blogId);//查询parent_comment_id为空的一级评论 combineChildren(comments1); System.out.println("一级评论"+comments1); return comments1; } private void combineChildren(List<Comment> comments) {//此处接收一级评论 for (Comment comment : comments) { List<Comment> replys1 = commentMapper.selectCommentByParent(comment.getId());//根据一级评论的id找出对应的二级目录 for(Comment reply1 : replys1) { //循环迭代,找出子代,存放在tempReplys中 recursively(reply1); } //修改顶级节点的reply集合为迭代处理后的集合 comment.setReplyComment(tempReplys); //清除临时存放区 tempReplys = new ArrayList<>(); } } //存放迭代找出的所有子代的集合 private List<Comment> tempReplys = new ArrayList<>(); private void recursively(Comment comment) { tempReplys.add(comment);//二级评论存放在临时list中 List<Comment> comment3 = commentMapper.selectCommentByParent(comment.getId());//根据二级评论的id找出是否有对应的三级评论 if (comment3.size()>0) {//如果存在三级评论继续根据三级评论遍历 for (Comment reply : comment3) { recursively(reply); // tempReplys.add(reply);//把查出来的三级评论存放到临时list中 // List<Comment> comments4 = commentMapper.selectCommentByParent(reply.getId()); // if (comments4.size()>0) {//如果四级评论还存在一直遍历 // recursively(reply); // } } } } }
这里是引用
1.数字和字符串之间的转化
数字转字符串:String.valueOf(i)
字符串转数字:Integer.parseInt(str)
2.批量查询的动态sql
<!-- 根据剧典id list查询剧典 -->
<select id="selectByIds" resultMap="DramaImageResultMap">
select * from drama where drama_id in
<foreach collection="dramaIds" item="dramaId" open="(" close=")" separator=",">
#{dramaId}
</foreach>
</select>
由于是博客和标签是多对多的情况,所以在新增一篇博客时需要对三个表同时做变动,即博客表,标签表,关联表,下面我将分别从每个表的角度描述“新增一个博客的实现过程”。
需要说明的一点是:在博客表中我存放标签id的形式是以“字符串的形式”,即一个表字段对应多个标签id
当我们新增一个博客,点击发布后,多个标签会以字符串的形式传递到服务器,然后让其映射实体对象接收,通过持久层存到放到数据库中,此过程并无特殊操作部分。
当新增完博客后,接着需要新增对应的标签和关联表,所以关键问题是如何将字符串类型的标签集,转化成数值型的标签集合。
拿到标签id转化为数值型集合
使用String的split方法将带有逗号分隔符的“字符串标签集合”分割成单个字符,然后通过Integer.parseInt()方法转换成数值型,通过遍历转化成的数值型,这个时候去做相对应的“标签表”和“关联表”的操作
每新增一个博客就需要增加一组相对应的id关联
删除操作就容易多了,拿到博客的id,查出与之对应的标签id集合(本篇只针对标签,不含分类模块),根据博客id删除博客,根据标签id集合删除对应“关联表”中的对应关系
除此之外还需要根据标签id在“标签表中”将与标签id对应的“博客数”减一
之所以单独将“博客修改”拎出来记录,是因为该模块在操作时比较复杂,而且所有的逻辑代码全是按照自己的思路来的,没有参考标准,我不知道自己这样写是否为最优写法,但是能把该功能跑起来,同时经过多次测试后没有出现错误,还是值得记录一下的。
下面描述一下该模块的正常执行逻辑过程:(只针对标签,不含其他内容)
当我们在修改完标签后,点击提交,在controller中拿到“修改后的”标签,需要判断那些是新增的,那些是舍弃掉的,那些是没有被修改的。
对于新增的: 拿到新增标签对应的id,到“标签表”中给“与之对应的博客数加一”,到“关联表”中新增一组对应关系。
对于舍弃的: 拿到舍弃标签对应的id,到“标签表”中给“与之对应的博客数减一”,到“关联表”中删除一组对应关系。
对于不变的: 保持不变,不做任何操作。
所以该问题的核心就是如何筛选出那些是新增,那些是舍弃,那些是不变的,那么如何将多个标签集合与另一个含有多个标签集合进行对比,然后做出正确的筛选呢?
解决思路:
- 在点击“编辑”进入编辑页面时,根据博客id拿到“字符串形式的标签集合”赋给controller总的静态变量作为“旧的标签集合”,然后再编辑后提交,这时再拿到修改后的“新的标签集合”。
2.将字符串形式的标签集合转化为数值list
3.在找出修改前后需要两个步骤
(1)先遍历旧list,然后再新list中查询,如果新list中包含旧的“标签id”,说明该id没有被换掉,如果新list中不含“标签id”就说明该id是新增的,对于新增的,上文有提到,不做重复。
(2)再遍历新list,然后再旧list中查询,如果旧list中包含旧的“标签id”,说明该id没有被换掉,如果新旧list中不含“标签id”就说明该id是被舍弃掉的,对于舍弃的,上文有提到,不做重复。
具体实现细节如下;
记录博客保存主要是涉及到前端的页面展示和代码优化,最主要的是第一遍做的时候我忽略掉了“和前端展示相关的部分”,最后在测试的时候发现有问题,而且还不小,需要对博客新增以及编辑博客的接口进行重构,当时也是全按照自己思路来的,不知道是否为最优,如果有大佬发现我的代码有需要改进的地方,还请不吝赐教,颜某不胜感激。
保存实际上是将编辑的博客以草稿的形式保存,既然是草稿,那就不应该将该草稿展示在前端页面,包括(关联表,标签,分类数据)。
那我当时遇到的问题是什么呢?
既然草稿不能展示在前端页面,那么就不能把相关的分类,标签和关联表的数据存入到数据库中。然后再次编辑发布之后,此时对于之前的草稿是一个“编辑”的操作,同时由“草稿状态”变为“发布状态”,此时博客会直接进入编辑的接口,不再去做添加操作,这样一来,该篇博客的(关联表,标签,分类数据)从始至终都未被存入至数据库中。
如何解决呢?
根据“是否发布”的状态判断
封装方法aboutPublished()
在客户端“标签”一栏中,当点击某个标签时,查询出所有关于这个标签的博客,以列表形式呈现,以后台逻辑为主
pageinfo不能正常显示查询到的总记录数
经过一番跟踪检查,和sql语句中foreach相关,具体原因,看的不是很理解1.使用foreach标签hi
参考资料:
https://blog.csdn.net/douzhenwen/article/details/112025079
https://www.jianshu.com/p/af138ede8580
使用子查询
(1).不在需要先到blog_relation_tag关联表中查出相关list,在传入到blog表中
(2).而是直接在查询博客集合的sql中添加子查询,把步骤(1)交给子查询完成即可
使用单例模式生成id
private static int getSYN_BLOGID(){
if (SYN_BLOGID == null){
SYN_BLOGID=;
return SYN_BLOGID;
}
return SYN_BLOGID;
}

在新增博客时,由于博客和标签是多对多的关系,所以需要id关联表来维护两个表的关系,但是在新增博客时一般情况下,id为null,那么如何获取到blog_id和tag_id关联呢?
mybatis在mapper映射文件中提供了一个
<insert id="insertSelective"
useGeneratedKeys="true" //允许JDBC支持自动生成主键
keyProperty="id" //自动映射到对象中的id属性
parameterType="com.lrm.po.Blog">
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。