Java内存马之SpringMVC开发基础

Java内存马之SpringMVC开发基础
Natro92简单Spring项目
开一个简单的Spring项目,设置Server URL为https://start.aliyun.com/spring.io的启动没有java8,记得修改构建模式为Maven。选择一个基础的Spring Web依赖就可以。
然后为其添加一个TestController
1 | package com.natro92.springtest.demos.web; |
运行SpringTestApplication.java
再加一个TestInterceptor
1 | package com.natro92.springtest.demos.web; |
再来一个WebConfig
1 | package com.natro92.springtest.demos.web; |
这样我们访问127.0.0.1:8080/?cmd=whoami就有会显了:
基于Netty的Spring WebFlux Demo
选择对应的Reactive Web
添加一个软件包,其中放置一会要用的两个java文件:GreetingHandler.java和GreetingRouter.java
1 | package com.natro92.webfluxdemo.hello; |
1 | package com.natro92.webfluxdemo.hello; |
然后再main/resources中的application.properties(没有就新建),修改server.port控制netty服务。
1 | server.port=9191 |
然后运行WebFluxDemoApplication访问即可。
PS: 可以通过以下的这个项目进一步认识Netty+SpringWebFlux:
初始Spring MVC
**Spring MVC** 是 Spring 框架的一部分,它提供了一个基于模型-视图-控制器 (MVC) 设计模式的 Web 应用程序开发框架。它简化了 Web 应用程序的开发,并提供了许多强大的功能。
DispatcherServlet是前端控制器,接受Request并分配给其他组件。HandlerMapping负责完成url到Controller映射,可以通过它来找到对应的处理Request的Controller;Controller处理Request,并返回ModelAndVIew对象,ModelAndView是封装结果视图的组件;- ④~⑦表示视图解析器解析
ModelAndView对象并返回对应的视图给客户端。
文章中还提到了IOC(Inverse of Control,控制反转)是一种设计模式,它通过将控制权从应用程序代码转移到框架来管理对象之间的依赖关系。在Spring MVC中,IOC容器负责实例化、配置和管理应用程序中的对象,而不是由开发人员来手动管理对象之间的依赖关系。这样可以降低组件之间的耦合度,使代码更加灵活、可维护和可测试。IOC容器在Spring MVC中通过依赖注入(Dependency Injection)来实现,将对象的依赖关系注入到对象中,从而实现控制反转的效果。
以下是两种常见的容器:
BeanFactory:Spring的最基本的IOC容器,提供了基本的IOC功能,只有在第一次请求时才创建对象。ApplicationContext:这是BeanFactory的扩展,提供了更多的企业级功能。ApplicationContext在容器启动时就预加载并初始化所有的单例对象,这样就可以提供更快的访问速度。
SpringMVC九大组件
DispatcherServlet(调度器Servlet):是Spring MVC的核心,负责接收客户端的请求并将请求分发给对应的处理器(Controller)进行处理。HandlerMapping(处理器映射器):负责将请求映射到对应的处理器(Controller)上,确定哪个处理器处理哪个请求。HandlerAdapter(处理器适配器):负责调用处理器(Controller)的方法来处理请求,并将处理结果返回给DispatcherServlet。Handler(处理器Controller):处理请求的核心组件,包含处理请求的方法。ViewResolver(视图解析器):负责根据处理器返回的逻辑视图名解析出具体的视图对象,用于渲染页面。View(视图):负责将模型数据渲染成最终的页面展示给用户。LocaleResolver(区域解析器):负责解析客户端的区域信息,用于国际化和本地化。ThemeResolver(主题解析器):负责解析客户端的主题信息,用于实现页面主题的切换。MultipartResolver(文件上传解析器):负责处理文件上传请求,将上传的文件转换成MultipartFile对象供处理器使用。
简单源码分析
九大组件初始化
先找到org.springframework.web.servlet.DispatcherServlet,这其中没有init函数,而init函数在父类FrameworkServlet的父类org.springframework.web.servlet.HttpServletBean中。
从ServletConfig中获取参数,创建一个PropertyValues,设置Bean属性,然后初始化ServletBean:
其中为空,应该是被Override了,可以在org.springframework.web.servlet.FrameworkServlet#initServletBean中看到,仔细查看530行部分,这里初始化了IOC容器。
而调用的org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext其中调用了一个onfresh方法:
同样是Override得到,我们查找上一层在org.springframework.web.servlet.DispatcherServlet#onRefresh
这就是SpringMVC的九大组件的初始化。
url和Controller关系
如何通过注解比如@RequestMapping("/")与方法关联。
从初始化这里入手吗,进入org.springframework.web.servlet.DispatcherServlet#initHandlerMappings
查找 ApplicationContext 中的所有 HandlerMappings,包括ancestor contexts。如果有,将所有machingBeans排序,并将最后得到的列表传给this.handlerMappings。
如果没获取到,注释说的很清楚:
Ensure we have at least one HandlerMapping, by registering a default HandlerMapping if no other mappings are found.
那么会用org.springframework.web.servlet.DispatcherServlet#getDefaultStrategies创建一个默认的HandlerMapping,并传值给this.handlerMappings。
进去看看getDefaultStrategies函数。
首先是从参数文件中加载默认的策略保存在defaultStrategies中,键值对中的值还要分割成数组,然后对每个类名进行如下操作:
- 先
ClassUtils.forName加载这个类。 - 实例化,添加策略,并返回这个值。
而这其中的DEFAULT_STRATEGIES_PATH名字就是DispatcherServlet.properties,根据注释这个部分定义了默认策略。
Name of the class path resource (relative to the DispatcherServlet class) that defines DispatcherServlet’s default strategy names.
在SpringMVC的jar包中可以看到配置文件:
而按照要求BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctionMapping,一般使用的是第二个。org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping我们跳转看下。
它的父类的父类org.springframework.web.servlet.handler.AbstractHandlerMethodMapping实现了InitializingBean,这个接口在bean初始化之后会进行一些自定义初始化逻辑。
在AbstractHandlerMapping中重写内容。
调用了initHandlerMethods,根据注释,扫描、检测、注册handler methods。
在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods这里断点。
进到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean中。
根据注释这个isHandler是判断类型是不是Handler,是用来检测给定的beanType类是否带有Controller注解或者RequestMapping注解。
然后调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods部分。
主要是使用ApplicationContext获取类型,否则返回handler类型。
获取处理器的用户类,能获取到实际处理请求的类。调用这里的selectMethods类。然后调用那个getMappingForMethod方法。
查看子类实现org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod
解析Controller类方法中的注解,然后生成一个RequestMappingInfo对象,然后我们进去看下org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(java.lang.reflect.AnnotatedElement):
这里info保存的路由是/user。
再看org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods,这里的lambda表达式的意思是:
意思是,先用selectInvocableMethod方法根据method和userType选择出一个可调用的方法,这样是为了处理可能存在的代理和AOP的情况,确保获取到的是可直接调用的原始方法;然后把bean、Method和RequestMappingInfo注册进MappingRegistry。
这样就连接上url和controller。
















































