2.19周报-爱代码爱编程
SourceURL:file:///home/mrl/桌面/2.19周报.doc
周报
代码行数:
周一 | 538 |
周二 | 546 |
周三 | 640 |
周四 | 599 |
周五 | 609 |
周六 | 644 |
周日 | 532 |
遇到的问题:
Spring boot国际化支持
当我们web项目涉及到国外部署或者国外用户使用时,需要展示不同语言信息,所以就需要国际化支持,下面将讲解Springboot国际化支持操作
1. 修改Springboot application.yml配置
spring:
messages:
basename: i18n/messages #配置国际化资源文件路径
2. 创建国际化资源文件
3. 创建SpringUtil类
创建SpringUtil工具类,方便其他工具类(未被Spring管理的class)获取Spring容器中的实例。
4. 创建MessageUtils工具类,获取message
5. java代码中使用MessageUtils实现国际化操作
请求测试接口返回结果:
6. 添加国际化拦截器
如图:
中文乱码问题:
没继承BaseController
把logback.xml改为log-back.xml即可
1问题描述
今天使用IDEA右下角提示内存不足
Low Memory
The IDE is running low on memory and this might affect performance. Please consider increasing available heap.
如下图所示
2 分析问题
这是因为提示Java内存不足了,我们去增加内存即可
3 解决步骤
这里以IDEA2022版本为例
在IDE中 帮助(help)–>change memory setting(改变内存设置)
默认应该是1024,我们改成2048
保存并重启即可
简单表述:控制台出现了这个异常:Error resolving template "xxx", template might not exist or might not be accessible by any of the configured Template Resolvers
解决办法:在报出这个异常的方法上添加注解@ResponseBody
讲一下个人理解:这个注解就是用来区别 方法的返回值字符串 和 视图解析器解析的页面名字字符串 的冲突的,举个例子:方法A返回的字符串success,如果和ajax的回调函数里的msg匹配,就弹出一个“执行成功”,因为success只是普通的字符串,所以要在方法上边加上@ResponseBody。 方法B的返回值是“/contextList”,而这个就是一个页面contextList.jsp或者是contextList.html(具体是什么看配置,反正是个页面)。
@ResponseBody 这个注解, 就表明该方法的返回值直接写入到 HTTP Response Body 中。 这就是说,如果返回的是JSON, 就得必须添加 @ResponseBody 这个注解,一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@responsebody后,会直接返回json数据。
网上找的资料(博客链接:注解错误:Error resolving template "xxx", template might not exist or might not be accessible b_Soar Tu的博客-CSDN博客):
controller层加注解@Controller 和@RestController都可以在前端调通接口,但是二者的区别在于,当用前者的时候在方法上必须添加注解@ResponseBody,如果不添加@ResponseBody,就会报上面错误,因为当使用@Controller 注解时,spring默认方法返回的是view对象(页面)。而加上@ResponseBody,则方法返回的就是具体对象了。@RestController的作用就相当于@Controller+@ResponseBody的结合体
遇到问题:
起因:启动项目时,,端口号被占用
解决:
1. 查看自己项目的端口号情况:
netstat -alnp | grep 8989 1
2. 发现被占用,杀掉进程(-9:是强制终止)
kill -9 4326 1
3.然后可以再查看一下端口号的情况
netstat -alnp | grep 8989 1
.gitignore不文件生效
原因是.gitignore只能忽略那些原来没有被追踪的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。那么解决方法就是先把本地缓存删除(改变成未被追踪状态),然后再提交:
git rm -r --cached .
git add .
git commit -m 'update .gitignore'
git push -u origin master
注意:
1、.gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。
2、想要.gitignore起作用,必须要在这些文件不在暂存区中才可以,.gitignore文件只是忽略没有被staged(cached)文件, 对于已经被staged文件,加入ignore文件时一定要先从staged移除,才可以忽略。
做算法题,逻辑很简单,但是运行超时,
把for循环中的
定义成long解决
vue知识点
异常处理的三种方式
1|0一. Controller层面上异常处理 @ExceptionHandler
说明:针对可能出问题的Controller,新增注解方法@ExceptionHandler.
@Controller @RequestMapping("/testController") publicclassTestController { @RequestMapping("/demo1") @ResponseBody publicObject demo1(){ inti = 1/ 0; returnnewDate(); } @ExceptionHandler({RuntimeException.class}) publicModelAndView fix(Exception ex){ System.out.println("do This"); returnnewModelAndView("error",newModelMap("ex",ex.getMessage())); } } |
注意事项: 1. 一个Controller下多个@ExceptionHandler上的异常类型不能出现一样的,否则运行时抛异常.
Ambiguous @ExceptionHandler method mapped for;
2. @ExceptionHandler下方法返回值类型支持多种,常见的ModelAndView,@ResponseBody注解标注,ResponseEntity等类型都OK.
戳我跳过原理,查看异常捕获方式二.
原理说明:
代码片段位于:org.springframework.web.servlet.DispatcherServlet#doDispatch
执行@RequestMapping方法抛出异常后,Spring框架 try-catch的方法捕获异常, 正常逻辑发不发生异常都会走processDispatchResult流程 ,区别在于异常的参数是否为null .
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | HandlerExecutionChain mappedHandler = null; Exception dispatchException = null; ModelAndView mv = null; try{ mappedHandler=getHandler(request); //根据请求查找handlerMapping找到controller HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//找到处理器适配器HandlerAdapter if(!mappedHandler.applyPreHandle(request,response)){ //拦截器preHandle return; } mv=ha.handle(request,response); //调用处理器适配器执行@RequestMapping方法 mappedHandler.applyPostHandle(request,response,mv); //拦截器postHandle }catch(Exception ex){ dispatchException=ex; } processDispatchResult(request,response,mappedHandler,mv,dispatchException) //将异常信息传入了 |
代码片段位于:org.springframework.web.servlet.DispatcherServlet#processDispatchResult
如果@RequestMapping方法抛出异常,拦截器的postHandle方法不执行,进入 processDispatchResult,判断入参 dispatchException,不为null , 代表发生异常,调用processHandlerException处理,
代码片段位于:org.springframework.web.servlet.DispatcherServlet#processHandlerException
this当前对象指dispatchServlet,handlerExceptionResolvers可以看到有三个HandlerExceptionResolver, 这三个是帮我们注册的. 遍历有序集合handlerExceptionResolvers,调用接口的resolveException方法.
记录注册的第一个 HandlerExceptionResolver : ExceptionHandlerExceptionResolver, 继承关系如下面所示.
代码片段位于:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#resolveException
AbstractHandlerExceptionResolver 和 AbstractHandlerMethodExceptionResolver名字看起来非常相似. 这里AbstractHandlerExceptionResolver 的shouldApplyTo都返回 true, logException用来记录日志、prepareResponse方法用来设置response的Cache-Control. 异常处理方法就位于doResolveException.
代码片段位于:org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver#shouldApplyTo
接口方法实现是AbstractHandlerExceptionResolver的resolveException,先判断 shouldApplyTo, AbstractHandlerExceptionResolver 和子类AbstractHandlerMethodExceptionResolver都实现了shouldApplyTo方法,子类的shouldApplyTo都调用父类AbstractHandlerExceptionResolver的shouldApplyTo.
查看父类AbstractHandlerExceptionResolver的shouldApplyTo方法.
代码片段位于:org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#shouldApplyTo
Spring初始化的时候并没有额外配置 , 所以mappedHandlers和mappedHandlerClasses都为null, 可以在这块扩展进行筛选 ,AbstractHandlerExceptionResolver提供了 setMappedHandlerClasses 、setMappedHandlers用于扩展.
代码片段位于:org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver#doResolveException
代码片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#doResolveHandlerMethodException
似曾相识的ServletInvocableHandlerMethod,getExceptionHandlerMethod目的就是获取 针对异常的处理方法,没找到的话这里就直接返回了,找到了执行异常处理方法;
之后同Spring请求方法执行一样的处理方式,设置argumentResolvers、returnValueHandlers,之后进行调用异常处理方法,
@ExceptionHandler的方法入参支持:Exception ;SessionAttribute 、 RequestAttribute注解 ; HttpServletRequest 、HttpServletResponse、HttpSession.
@ExceptionHandler方法返回值常见的可以是: ModelAndView 、@ResponseBody注解、ResponseEntity;
getExceptionHandlerMethod说明: 获取对应的@ExceptionHandler方法,封装成ServletInvocableHandlerMethod返回.
代码片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#getExceptionHandlerMethod
exceptionHandlerCache是针对Controller层面的@ExceptionHandler的处理方式,而exceptionHandlerAdviceCache是针对@ControllerAdvice的处理方式. 这两个属性都位于ExceptionHandlerExceptionResolver中.
handlerType指代Controller的class属性,尝试从缓存A exceptionHandlerCache 中根据controller的class 查找ExceptionHandlerMethodResolver; 缓存A之前没存储过Controller的class ,所以新建一个ExceptionHandlerMethodResolver 加入缓存中. ExceptionHandlerMethodResolver 的初始化工作一定做了某些工作!
resolveMethod方法:根据异常对象让 ExceptionHandlerMethodResolver 解析得到 method , 匹配到异常处理方法 就直接封装成对象 ServletInvocableHandlerMethod ; 就不会再去走@ControllerAdvice里的异常处理器了. 这里说明了,ExceptionHandlerMethodResolver 初始化的时候完成存储 @ExceptionHandler.
查看ExceptionHandlerMethodResolver 初始化工作内容:
代码片段位于:org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#ExceptionHandlerMethodResolver
handlerType为传入的Controller的class属性,通过EXCEPTION_HANDLER_METHODS选出 class 中标注@ExceptionHandler的方法,解析@Exception注解的value值(class类型的数组),并加入到当前ExceptionHandlerMethodResolver的mappedMethods集合中,key为 异常类型 ,value为 method.
如果@ExceptionHandler的 value属性为空,就会将方法入参中的Throwable的子类作为异常类型. @ExceptionHandler的value属性和方法入参不能同时都为空,否则会抛出异常.
ExceptionHandlerMethodResolver完成了初始化工作,如何根据当前发生异常类型查找到对应方法?
代码片段位于:org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#resolveMethod
resolveMethodByExceptionType根据当前抛出异常寻找 匹配的方法,并且做了缓存,以后遇到同样的异常可以直接走缓存取出method,
代码片段位于:org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#resolveMethodByExceptionType
resolveMethodByExceptionType方法,尝试从缓存A:exceptionLookupCache中根据 异常class类型获取Method ,初始时候肯定缓存为空 ,就去 遍历ExceptionHandlerMethodResolver的mappedMethods(上面提及了key为异常类型,value为method), exceptionType为当前@RequestMapping方法抛出的异常,判断当前异常类型是不是@ExceptionHandler中value声明的子类或本身,满足条件就代表匹配上了;可能存在多个匹配的方法,使用ExceptionDepthComparator排序,排序规则是按照继承顺序来(继承关系越靠近数值越小,当前类最小为0,顶级父类Throwable为int最大值),排序之后选取继承关系最靠近的那个,并且存入ExceptionHandlerMethodResolver的exceptionLookupCache中,key为当前抛出的异常,value为解析出来的匹配method.
至此 @ExceptionHandler Spring读取到并解析出来完毕了,后续流程和Spring正常请求流程一样,包括@ExceptionHandler的方法入参、方法返回值.
@ExceptionHandler的方法入参支持:Exception ;SessionAttribute 、 RequestAttribute注解 ; HttpServletRequest 、HttpServletResponse、HttpSession.
@ExceptionHandler方法返回值常见的可以是: ModelAndView 、@ResponseBody注解、ResponseEntity;
2|0二. 全局级别异常处理器 实现HandlerExceptionResolver接口
01 02 03 04 05 06 07 08 09 10 11 | publicclassMyHandlerExceptionResolver implementsHandlerExceptionResolver { @Override publicModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("发生全局异常!"); ModelMap mmp=newModelMap(); mmp.addAttribute("ex",ex.getMessage()); returnnewModelAndView("error",mmp); } } |
使用方式: 只需要将该Bean加入到Spring容器,可以通过Xml配置,也可以通过注解方式加入容器;
方法返回值不为null才有意义,如果方法返回值为null,可能异常就没有被捕获.
缺点分析:比如这种方式全局异常处理返回JSP、velocity等视图比较方便,返回json或者xml等格式的响应就需要自己实现了.如下是我实现的发生全局异常返回JSON的简单例子.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | publicclassMyHandlerExceptionResolver implementsHandlerExceptionResolver { @Override publicModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("发生全局异常!"); ModelMap mmp=newModelMap(); mmp.addAttribute("ex",ex.getMessage()); response.addHeader("Content-Type","application/json;charset=UTF-8"); try{ newObjectMapper().writeValue(response.getWriter(),ex.getMessage()); response.getWriter().flush(); } catch(IOException e) { e.printStackTrace(); } returnnewModelAndView(); } } |
戳我跳过原理解释,查看方式三.
原理分析:记得之前介绍了 DispatcherServlet的HandlerExceptionResolver集合,这种方式的HandlerExceptionResolver就是从DispatcherServlet的HandlerExceptionResolver集合入手的.
代码片段位于:org.springframework.web.servlet.DispatcherServlet#processHandlerException
this对象指代DispatcherServlet,和上面方式对比,发现我们只是将MyHandlerExceptionResolver 加入到Spring容器,dispatchServlet 的 handlerExceptionResolvers属性就多了我们自己定义的全局异常解析器;
ExceptionHandlerMethodResolver是用来解析@Controller层面的@ExceptionHandler注解,当前Controller没有找到@ExceptionHandler来处理自己抛出的异常,才遍历下一个HandlerExceptionResolver;
HandlerExceptionResolver是个有序集合,Spring注册的HandlerExceptionResolver调用resolveException都失败之后,才轮到我们自定义的MyHandlerExceptionResolver ;而且我们自定义的MyHandlerExceptionResolver 就没法使用SpringMvc的注解等等.
我们只是将HandlerExceptionResolver加入到Spring容器中,Spring是如何通知给DispatcherServlet呢?
代码片段位于:org.springframework.web.servlet.DispatcherServlet#initHandlerExceptionResolvers
initHandlerExceptionResolvers只是DispatcherServlet初始化策略方法initStrategies中的一小步,可以看到只要是SpringMvc父子容器中注册的HandlerExceptionResolver类型实例,DispatcherServlet都会自动将其加入到DispatcherServlet的handlerExceptionResolvers中. 所以我们需要做的只是实现HandlerExceptionResolver接口,并且纳入Spring容器管理即可.
3|0三.全局级别异常处理器 @ControllerAdvice+@ExceptionHandler
简单使用方法:
01 02 03 04 05 06 07 08 09 10 11 | @ControllerAdvice publicclassGlobalController { @ExceptionHandler(RuntimeException.class) publicModelAndView fix1(Exception e){ System.out.println("全局的异常处理器"); ModelMap mmp=newModelMap(); mmp.addAttribute("ex",e); returnnewModelAndView("error",mmp); } } |
用法说明: 这种情况下 @ExceptionHandler 与第一种方式用法相同,返回值支持ModelAndView,@ResponseBody等多种形式.
方式一提到ExceptionHandlerExceptionResolver不仅维护@Controller级别的@ExceptionHandler,同时还维护的@ControllerAdvice级别的@ExceptionHandler.
代码片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#getExceptionHandlerMethod
isApplicableToBeanType方法是用来做条件判断的,@ControllerAdvice注解有很多属性用来设置条件,basePackageClasses、assignableTypes、annotations等,比如我限定了annotations为注解X, 那标注了@X 的ControllerA就可以走这个异常处理器,ControllerB就不能走这个异常处理器.
现在问题的关键就只剩下了exceptionHandlerAdviceCache是什么时候扫描@ControllerAdvice的,下面的逻辑和@ExceptionHandler的逻辑一样了.
exceptionHandlerAdviceCache初始化逻辑:
代码片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#afterPropertiesSet
afterPropertiesSet是Spring bean创建过程中一个重要环节.
代码片段位于:org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache
ControllerAdviceBean.findAnnotatedBeans方法查找了SpringMvc父子容器中标注 @ControllerAdvice 的bean, new ExceptionHandlerMethodResolver初始化时候解析了当前的@ControllerAdvice的bean的@ExceptionHandler,加入到ExceptionHandlerExceptionResolver的exceptionHandlerAdviceCache中,key为ControllerAdviceBean,value为ExceptionHandlerMethodResolver . 到这里exceptionHandlerAdviceCache就初始化完毕.
查找SpringMvc父子容器中所有@ControllerAdivce的bean的方法
代码片段位于:org.springframework.web.method.ControllerAdviceBean#findAnnotatedBeans
遍历了SpringMVC父子容器中所有的bean,标注ControllerAdvice注解的bean加入集合返回.
4|0四.比较说明.
@Controller+@ExceptionHandler、HandlerExceptionResolver接口形式、@ControllerAdvice+@ExceptionHandler优缺点说明:
在Spring4.3.0版本下,1.优先级来说,@Controller+@ExceptionHandler优先级最高,其次是@ControllerAdvice+@ExceptionHandler,最后才是HandlerExceptionResolver,说明假设三种方式并存的情况 优先级越高的越先选择,而且被一个捕获处理了就不去执行其他的.
2. 三种方式都支持多种返回类型,@Controller+@ExceptionHandler、@ControllerAdvice+@ExceptionHandler可以使用Spring支持的@ResponseBody、ResponseEntity,而HandlerExceptionResolver方法声明返回值类型只能是 ModelAndView,如果需要返回JSON、xml等需要自己实现.
3.缓存利用,@Controller+@ExceptionHandler的缓存信息在ExceptionHandlerExceptionResolver的exceptionHandlerCache,@ControllerAdvice+@ExceptionHandler的缓存信息在ExceptionHandlerExceptionResolver的exceptionHandlerAdviceCache中, 而HandlerExceptionResolver接口是不做缓存的,在前面两种方式都fail的情况下才会走自己的HandlerExceptionResolver实现类,多少有点性能损耗.
心得体会:
这周终于有了些正经工作的样子,有的前辈忙不过来的工作也可以托付给我,当然有不会的地方也要请教,最后在与前辈的协作下共同完成本周的工作,另外毕业论文也要开始筹备了。