【Spring】Spring统一功能处理

Spring统一功能处理

  • 拦截器
    • 拦截器
      • 什么是拦截器
      • 拦截器的基本使用
        • 定义拦截器
        • 注册配置拦截器
      • 拦截器详解
        • 拦截器的拦截路径配置
        • 拦截器实现原理
          • 初始化
          • 处理请求
      • 适配器模式
  • 统一数据返回格式
    • 统一数据返回格式快速入门
  • 统一异常处理

拦截器

场景: 我们要对一个网站实现强制登陆的功能,后端根据Session来判断用户是否登录,但是如果我们要这样实现,就需要对每一个接口都增加这样的逻辑处理 此时就比较麻烦

• 需要修改每个接⼝的处理逻辑
• 需要修改每个接⼝的返回结果
• 接⼝定义修改, 前端代码也需要跟着修改

有没有更简单的办法, 统⼀拦截所有的请求, 并进⾏Session校验呢, 这⾥我们学习⼀种新的解决办法: 拦截器

拦截器

什么是拦截器

拦截器是Spring框架提供的核⼼功能之⼀, 主要⽤来拦截⽤⼾的请求, 在指定⽅法前后, 根据业务需要执⾏预先设定的代码

也就是说, 允许开发⼈员提前预定义⼀些逻辑, 在⽤⼾的请求响应前后执⾏. 也可以在⽤⼾请求前阻⽌
其执⾏.
在拦截器当中,开发⼈员可以在应⽤程序中做⼀些通⽤性的操作, ⽐如通过拦截器来拦截前端发来的
请求, 判断Session中是否有登录⽤⼾的信息. 如果有就可以放⾏, 如果没有就进⾏拦截.

拦截器的基本使用

定义拦截器

实现HandleInterceptor接口 重写方法

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("LoginInterceptor 目标方法执行前");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("LoginInterceptor 目标方法执行后");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("LoginInterceptor 视图执行后");}
}

preHandle方法:目标方法执行前执行,返回true 继续执行后续操作,返回false 中断后续操作
postHandle方法:目标方法执行后执行
afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏

注册配置拦截器

实现WebMvcConfigurer接口 重写addInterceptors方法

@Configuration
public class WebConfig implements WebMvcConfigurer {//⾃定义的拦截器对象@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册⾃定义拦截器对象registry.addInterceptor(loginInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径}
}

拦截器详解

拦截器的拦截路径配置

拦截路径是指我们定义的这个拦截器, 对哪些请求⽣效.我们在注册配置拦截器的时候, 通过 addPathPatterns()⽅法指定要拦截哪些请求. 也可以通过excludePathPatterns() 指定不拦截哪些请求

@Configuration
public class WebConfig implements WebMvcConfigurer {//自定义拦截器对象@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//设置拦截器的请求路径// /**表示拦截所有registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login").excludePathPatterns("/**/*.js").excludePathPatterns("/**/*.css").excludePathPatterns("/**/*.png").excludePathPatterns("/**/*.html");//addPath设置拦截那些请求//excludePath设置不拦截哪些请求}
}

在拦截器中除了可以设置 /** 拦截所有资源外,还有⼀些常⻅拦截路径设置:

拦截路径含义举例
/*一级路径能匹配/user,/book,/login,不能匹配 /user/login
/**任意级路径能匹配/user,/user/login,/user/reg
/book/*/book下的⼀级路径能匹配/book/addBook,不能匹配/book/addBook/1,/book
/book/**/book下的任意级路径能匹配/book,/book/addBook,/book/addBook/2,不能匹配/user/login

添加拦截器后, 执⾏Controller的⽅法之前, 请求会先被拦截器拦截住. 执⾏ preHandle() ⽅法,
这个⽅法需要返回⼀个布尔类型的值.
如果返回true, 就表⽰放⾏本次操作, 继续访问controller中的⽅法.
如果返回false,则不会放⾏(controller中的⽅法也不会执⾏).

controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 这个⽅法以及afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据.

拦截器实现原理

当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet, 它来控制程序的执⾏顺序.

所有请求都会先进到DispatcherServlet,执⾏doDispatch 调度⽅法. 如果有拦截器,会先执⾏拦截器preHandle() ⽅法的代码, 如果 preHandle() 返回true, 继续访问controller中的⽅法.controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 和 afterCompletion(),返回给DispatcherServlet, 最终给浏览器响应数据.
在这里插入图片描述

初始化

DispatcherServlet的初始化⽅法 init() 在其⽗类 HttpServletBean 中实现的.

主要作⽤是加载 web.xml 中 DispatcherServlet 的 配置, 并调⽤⼦类的初始化.
在这里插入图片描述

在 HttpServletBean 的 init() 中调⽤了 initServletBean() , 它是在FrameworkServlet 类中实现的, 主要作⽤是建⽴ WebApplicationContext 容器(有时也称上下⽂), 并加载 SpringMVC 配置⽂件中定义的 Bean到该容器中, 最后将该容器添加到 ServletContext 中. 下⾯是initServletBean() 的具体代码:

在这里插入图片描述
初始化web容器的过程中, 会通过onRefresh 来初始化SpringMVC的容器
在这里插入图片描述
在initStrategies()中进⾏9⼤组件的初始化, 如果没有配置相应的组件,就使⽤默认定义的组件(在
DispatcherServlet.properties中有配置默认的策略, ⼤致了解即可

在这里插入图片描述

⽅法initMultipartResolver、initLocaleResolver、initThemeResolver、initRequestToViewNameTranslator、initFlashMapManager的处理⽅式⼏乎都⼀样(1.2.3.7.8,9),从应⽤⽂中取出指定的Bean, 如果没有, 就使⽤默认的.⽅法initHandlerMappings、initHandlerAdapters、initHandlerExceptionResolvers的处理⽅式⼏乎都⼀样(4,5,6)

  1. 初始化处理器映射器HandlerMappings:处理器映射器作⽤,1)通过处理器映射器找到对应的处理器适配器,将请求交给适配器处理;2)缓存每个请求地址URL对应的位置(Controller.xxx⽅法);如果在ApplicationContext发现有HandlerMappings,则从ApplicationContext中获取到所有的HandlerMappings,并进⾏排序;如果在ApplicationContext中没有发现有处理器映射器,则默认BeanNameUrlHandlerMapping作为处理器映射器
  2. 初始化处理器适配器HandlerAdapter:作⽤是通过调⽤具体的⽅法来处理具体的请求;如果在ApplicationContext发现有handlerAdapter,则从ApplicationContext中获取到所有的 HandlerAdapter,并进⾏排序;如果在ApplicationContext中没有发现处理器适配器,则不设置异常处理器,则默认SimpleControllerHandlerAdapter作为处理器适配器
  3. 初始化异常处理器解析器HandlerExceptionResolver:如果在ApplicationContext发现有handlerExceptionResolver,则从ApplicationContext中获取到所有的HandlerExceptionResolver,并进⾏排序;如果在ApplicationContext中没有发现异常处理器解析器,则不设置异常处理器
处理请求
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);//1.获取执行链//遍历所有的HandlerMapper 找到与请求对应的HandlermappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}//2. 获取适配器//遍历所有的HandlerAdapter 找到可以处理该Handler的HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}//3. 执行拦截器的preHandler方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//4. 执行目标方法mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);//5. 执行拦截器的postHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}//6.处理视图 处理之后执⾏拦截器afterCompletion⽅法processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {//7.执⾏拦截器afterCompletion⽅法triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

此处最关键的就是3 4 5 点 这里规定了执行目标方法前执行preHandle 执行目标方法之后执行postHandle方法

适配器模式

适配器模式是一种设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。它允许原本不兼容的类能够合作无间。

适配器模式主要包括两个核心角色:目标接口(Target)和适配器(Adapter)。目标接口是客户端所期望的接口,适配器则是将原本不兼容的类转换成目标接口的中间层。

适配器模式可以通过两种方式实现:类适配器和对象适配器。在类适配器中,适配器继承了被适配类,并实现了目标接口。而在对象适配器中,适配器持有一个被适配对象的实例,并实现了目标接口。

使用适配器模式可以有以下几个好处:

  1. 可以让原本不兼容的类能够一起工作,提高代码的复用性。
  2. 可以封装已有的类,对外隐藏底层的实现细节。
  3. 可以在不修改现有代码的情况下引入新的功能。
    总之,适配器模式是一种常用的设计模式,可用于解决不同接口之间不兼容的问题,使得原本无法合作的类能够协同工作。

在这里插入图片描述
HandlerAdapter 在 Spring MVC 中使⽤了适配器模式

HandlerAdapter 主要⽤于⽀持不同类型的处理器(如 Controller、HttpRequestHandler 或者Servlet 等),让它们能够适配统⼀的请求处理流程。这样,Spring MVC 可以通过⼀个统⼀的接⼝来处理来⾃各种处理器的请求

场景: 前⾯学习的slf4j 就使⽤了适配器模式, slf4j提供了⼀系列打印⽇志的api, 底层调⽤的是log4j 或者logback来打⽇志, 我们作为调⽤者, 只需要调⽤slf4j的api就⾏了

//Slf4j接口
interface Slf4jApi{void log(String message);
}//log4j接口
class Log4j{void log4jLog(String message){System.out.println("Log4j打印:" + message);}
}//slf4j和log4j适配器class Slf4jLog4JAdapter implements Slf4jApi{private Log4j log4j;public Slf4jLog4JAdapter(Log4j log4j) {this.log4j = log4j;}@Overridepublic void log(String message) {log4j.log4jLog(message);}
}
public class Slf4jDemo {public static void main(String[] args) {Slf4jApi slf4jApi = new Slf4jLog4JAdapter(new Log4j());slf4jApi.log("使用slf4j打印日志");}
}

在这里插入图片描述
适配器模式应⽤场景
⼀般来说,适配器模式可以看作⼀种"补偿模式",⽤来补救设计上的缺陷. 应⽤这种模式算是"⽆奈之举", 如果在设计初期,我们就能协调规避接⼝不兼容的问题, 就不需要使⽤适配器模式了

所以适配器模式更多的应⽤场景主要是对正在运⾏的代码进⾏改造, 并且希望可以复⽤原有代码实现新的功能. ⽐如版本升级等

统一数据返回格式

强制登录案例中, 我们共做了两部分⼯作

  1. 通过Session来判断⽤⼾是否登录
  2. 对后端返回数据进⾏封装, 告知前端处理的结果

后端统一返回结果

package com.bite.book.model;import com.bite.book.enums.ResultCode;
import lombok.Data;@Data
public class Result<T> {/*** 业务状态码*/private ResultCode code;  //0-成功  -1 失败  -2 未登录/*** 错误信息*/private String errMsg;/*** 数据*/private T data;public static <T> Result<T> success(T data){Result result = new Result();result.setCode(ResultCode.SUCCESS);result.setErrMsg("");result.setData(data);return result;}public static <T> Result<T> fail(String errMsg){Result result = new Result();result.setCode(ResultCode.FAIL);result.setErrMsg(errMsg);result.setData(null);return result;}public static <T> Result<T> fail(String errMsg,Object data){Result result = new Result();result.setCode(ResultCode.FAIL);result.setErrMsg(errMsg);result.setData(data);return result;}public static <T> Result<T> unlogin(){Result result = new Result();result.setCode(ResultCode.UNLOGIN);result.setErrMsg("用户未登录");result.setData(null);return result;}}

后端返回接口

@RequestMapping("/getBookListByPage")public Result getBookListByPage(PageRequest pageRequest, HttpSession session){log.info("查询翻页信息, pageRequest:{}",pageRequest);//校验成功if (pageRequest.getPageSize()<0 || pageRequest.getCurrentPage()<1){return Result.fail("参数校验失败");}PageResult<BookInfo> bookInfoPageResult = null;try {bookInfoPageResult = bookService.selectBookInfoByPage(pageRequest);//此处对返回的数据进行再次封装return Result.success(bookInfoPageResult);}catch (Exception e){log.error("查询翻页信息错误,e:{}",e);return Result.fail(e.getMessage());}}

拦截器帮我们实现了第⼀个功能, 接下来看SpringBoot对第⼆个功能如何⽀持

统一数据返回格式快速入门

统一的数据返回格式使用@ControllerAdviceResponseBodyAdvice 的方式实现

@ControllerAdvice表示控制器通知类

添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加 @ControllerAdvice 注解

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowired//转jsonprivate ObjectMapper objectMapper;//判断是否要执行beforeBodyWrite方法//ture为执行//false不执行@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {return Result.success(body);}
}

supports方法: 判断是否要执行beforeBodyWrite方法, true为执行 false不执行, 通过该方法可以选择那些类或者哪些方法的response要进行处理 其他的不处理
beforeBodyWrite方法: 对response方法进行具体操作处理

统一异常处理

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器,两个结合表⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件

package com.bite.book.config;import com.bite.book.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice
@ResponseBody
@Slf4j
public class ErrorHandler {@ExceptionHandlerpublic Object handler(Exception e){log.info("发生异常 e:{}",e.getMessage());return Result.fail(e.getMessage());}@ExceptionHandlerpublic Object handler(NullPointerException e) {return Result.fail("发⽣NullPointerException:"+e.getMessage());}@ExceptionHandlerpublic Object handler(ArithmeticException e) {return Result.fail("发⽣ArithmeticException:"+e.getMessage());}
}

当有多个异常通知时,匹配顺序为当前类及其⼦类向上依次匹配

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/213262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChibiOS简介2/5

ChibiOS简介2/5 1. 源由2. ChibiOS基础知识2/52.4 Chapter 4 - ChibiOS General Architecture2.4.1 The Big Picture&#xff08;总体框图&#xff09;2.4.2 Embedded Components&#xff08;嵌入式组件&#xff09;2.4.3 Application Model&#xff08;应用模型&#xff09;2.…

爬虫解析——Xpath的安装及使用(五)

目录 一、Xpath插件的安装 二、安装 lxml 三、Xpath解析文件 1.解析本地文件 &#xff08;1&#xff09;导入本地文件 &#xff08;2&#xff09;解析本地文件 2.服务器文件解析 &#xff08;1&#xff09;获取网页源码 &#xff08;2&#xff09;解析服务器响应文件 …

TailwindCSS 如何处理RTL布局模式

背景 TikTok作为目前全世界最受欢迎的APP&#xff0c;需要考虑兼容全世界各个地区的本地化语言和阅读习惯。其中对于阿拉伯语、波斯语等语言的阅读书写习惯是从右向左的&#xff0c;在前端有一个专有名字RTL模式&#xff0c;即Right-to-Left。 其中以阿拉伯语作为第一语言的人…

建立个人学习观|地铁上的自习室

作者&#xff1a;向知 如果大家有机会来北京&#xff0c;可以来看看工作日早上八九点钟&#xff0c;15 号线从那座叫“顺义”的城市通向“望京”的地铁&#xff0c;你在那上面&#xff0c;能看到明明白白的&#xff0c;人们奔向梦想的模样。 一、地铁上的自习室 我在来北京之前…

【算法集训】基础数据结构:三、链表

链表就是将所有数据都用一个链子串起来&#xff0c;其中链表也有多种形式&#xff0c;包含单向链表、双向链表等&#xff1b; 现在毕竟还是基础阶段&#xff0c;就先学习单链表吧&#xff1b; 链表用头结点head表示一整个链表&#xff0c;每个链表的节点包含当前节点的值val和下…

2024 年顶级的 Android 系统修复软件与方法

您是否正在寻找可以修复 PC 上 Android 操作系统的工具&#xff1f;这是我们精选的最好的 Android 系统修复软件&#xff01; Android 是世界著名的智能手机操作系统。全世界有数百万人使用这个操作系统&#xff0c;这使得它安全可靠。然而&#xff0c;这仍然不能使它完美无缺…

048:利用vue-video-player播放m3u8

第048个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

MyBatis进阶之分页和延迟加载

文章目录 分页1. RowBounds 分页2. PageHelper 分页3. PageInfo 对象属性描述 延迟加载立即加载激进式延迟加载真-延迟加载 分页 Mybatis 中实现分页功能有 3 种途径&#xff1a; RowBounds 分页&#xff08;不建议使用&#xff09;Example 分页&#xff08;简单情况可用)Pag…

关于对向量检索研究的一些学习资料整理

官方学习资料 主要是的学习资料是&#xff0c; 官方文档 和官方博客。相关文章还是挺多 挺不错的 他们更新也比较及时。有最新的东西 都会更新出来。es scdn官方博客 这里简单列一些&#xff0c;还有一些其他的&#xff0c;大家自己感兴趣去看。 什么是向量数据库 Elasticse…

文件加密软件哪个最好用 好用的文件加密软件推荐

一说到文件加密软件&#xff0c;可能大家都会去搜一些不知名的软件来&#xff0c;但是选择这种加密软件&#xff0c;最好还是要看一些资质的。 资质不好的&#xff0c;可能加密过后你自己也打不开文件&#xff0c;&#xff08;ps&#xff1a;我自己就遇到过这种情况&#xff09…

基于Java SSM框架高校校园点餐订餐系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架高校校园点餐订餐系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

Oracle(2-14)User-Managed Incomplete Recovery

文章目录 一、基础知识1、Incomplete Recovery Overview 不完全恢复概述2、Situations Requiring IR 需要不完全恢复的情况3、Types of IR 不完全恢复的类型4、IR Guidelines 不完全恢复指南5、User-Managed Procedures 用户管理程序6、RECOVER Command Overview 恢复命令概述7…

Python数据科学视频讲解:Python注释

2.3 Python注释 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解2.3节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。内容涵盖数据科学应用的全流程&#xff0c;包括数据科学应用和…

20231210原始编译NanoPC-T4(RK3399)开发板的Android10的SDK

20231210原始编译NanoPC-T4(RK3399)开发板的Android10的SDK 2023/12/10 17:27 rootrootrootroot-X99-Turbo:~$ rootrootrootroot-X99-Turbo:~$ mkdir nanopc-t4 rootrootrootroot-X99-Turbo:~$ rootrootrootroot-X99-Turbo:~$ rootrootrootroot-X99-Turbo:~$ cd nanopc-t4/ …

【AIE】AIE微信合集

AIE微信合集 AIE(1) 对于Versal&#xff0c;我们从系统角度看&#xff0c;可将其分为3个Domain&#xff1a;AIE、PS和PL&#xff0c;如下图所示。如果要运行一个AIE的应用&#xff0c;绝大多数情况下&#xff0c;这3个Domain我们都会用到&#xff0c;使其协同工作。这里我们仅…

《绝地求生》新手怎么玩 游戏基本介绍

随着电竞热潮的兴起&#xff0c;《绝地求生》已经成为了一款备受玩家热爱的游戏。这款游戏在全球范围内拥有庞大的玩家群体&#xff0c;它将你置身于一个荒无人烟的岛屿上&#xff0c;与其他99名玩家展开生死竞争。作为一个新手&#xff0c;下面闲游盒小盒子就为大家详细介绍一…

写实3D游戏模型纹理贴图设置

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 当谈到游戏角色的3D模型风格时&#xff0c;有几种不同的风格&#xff1a; …

Mybatis源码解析5:Mapper执行流程1

Mybatis源码解析5&#xff1a;Mapper执行流程1 1.项目结构2. 源码分析2.1 Mapper代理 MapperProxy#invoke2.2 创建MapperMethod2.2.1 方法名称解析器ParamNameResolve2.2.2 MapperMethod#execute 2.3 DefaultSqlSession2.4 CachingExecutor2.5 SimpleExecutor#doQuery获取连接对…

Nacos源码解读09——配置中心配置信息创建修改怎么处理的

存储配置 从整体上Nacos服务端的配置存储分为三层&#xff1a; 内存&#xff1a;Nacos每个节点都在内存里缓存了配置&#xff0c;但是只包含配置的md5&#xff08;缓存配置文件太多了&#xff09;&#xff0c;所以内存级别的配置只能用于比较配置是否发生了变更&#xff0c;只用…

进行生成简单数字图片

1.之前只能做一些图像预测,我有个大胆的想法,如果神经网络正向就是预测图片的类别,如果我只有一个类别那就可以进行生成图片,专业术语叫做gan对抗网络 2.训练代码 import torch import torch.nn as nn import torch.optim as optim import torchvision.transforms as transfo…