SpringMVC学习经验
SpringMVC
各位读者朋友你好,我是你的好朋友IT黑铁,在最近一段时间里我学习了关于Spring的相关知识,今天就来继续分享,如有错误,还望指出!
学习途径:@黑马程序员bilibili视频
概念
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架。
什么是MVC模式与三层架构?基础 | 三层架构与MVC模式 - 知乎 (zhihu.com)
处理流程
相关注解
@Controller(表示当前类为表现层控制器)
@RequestMapping(映射放射路径)
@RequestBody(写在方法上:设置该方法返回值为响应内容,如果返回值是对象或其他引用类型则是响应json,是string则响应纯文本,如果不写该注解则是跳转返回值的页面(但非Converter接口子类,而是全新的HttpMessageConverter接口,依赖于解析工具jackson之类,默认情况下RequestMappingHandlerAdapter不会带该jackson转换器);写在形参上,将请求体的json数据转为对象)
@RequestParam(写在方法形参中,绑定请求头参数与形参关系,还可以将请求头参数作为集合的数据,解决集合无构造方法的问题)
@EnableWebMVC(根据类型匹配对应的类型转换器(Converter接口有默认转换规则,但某些转换没有开启,就使用此注解),例:开启json数据转为对象的功能)
@DateTimeFormat(默认标准时间格式为/,设置pattern规定其他格式)
@PathVariable(url路径参数,配合路径映射的占位${}使用)
@RESTController(等同于@Controller和@ResponseBody)
@PostMapping(等同于@RequestMapping(method=RequestMethod.Post),新增/保存)
@DeleteMapping(删除)
@GetMapping(查询)
@PutMapping(修改/更新)
@RESTControllerAdvice(REST风格的处理器)
@ControllerAdvice(非REST风格的处理器)
@ExceptionHandler(拦截指定异常后,执行被注方法)
@PropertySource(读取配置文件)
@EnableConfigurationProperties(绑定配置文件对应前缀的对象,使用该对象获取值,例:WebMvcProperties.class,ServerProperties.class)
关于外部Tomcat
servlet容器启动配置类(加载spring配置)
具体操作:第一种:实现AbstractDispatcherServletInitializer接口,实现三个基本方法:createServletApplicationContext(加载springMVC配置类,使用AnnotationConfigWebApplicationContext容器,并为其注册容器配置类),getServletMappings(设置springMVC容器处理路径),createRootApplicationContext(加载其他spring配置)。其他方法,设置过滤器:getServletFillters(其中spring-web内置CharacterEncodingFilter可以设置字符编码)第二种:实现AbstractAnnotationConfigDispatcherServletInitializer,对第一种的方式的简化,只需给出配置类。
注意:如果getServletMappings拦截了所有资源,则加载静态资源时springMVC将找不到对应mapping,所以该资源应该由tomcat处理。解决方案为:创建一个SpringMvcSupport配置类,继承WebMvcConfigurationSupport,实现其addResourceHandlers方法,当访问某目录时,走该目录下的内容,不被springMVC处理,registry.addResourceHandler.addResourceLocation(也可以只使用addResourceHandler设置两个目录)。当然也可以直接在springMVC核心配置类实现WebMvcConfigurer接口,简化开发,但侵入性较强。
异常处理器
作用:集中、统一的处理项目中的异常。
项目异常分类:业务异常、系统异常、其他异常。
具体实现:定义自定义异常(增加属性异常编号以便包装),将异常分类。系统异常:继承RuntimeException(出异常可以不处理自动向上抛),然后将可能出异常的地方抛异常,接着在异常处理器方法里完成如下三件事:记录日志、发送信息给运维、发送邮件给开发人员并将异常对象发送给开发人员(包装为数据包装对象后再返回)。
@ExceptionHandler内部原理:HandlerExpceptionResolver的一个子类ExceptionHandlerExceptionResolver,并为其添加一些组件,setMessageConverters(响应json转换)、afterPropertiesSet(容器内置bean会自动回调该方法,添加一些默认参数解析器和返回值解析器,非容器bean需要手动添加),包装方法映射HandlerMethod,调用ExceptionHandlerExceptionResolver的resolveException方法匹配该指定映射和异常类型是否匹配(包装异常依然能够匹配到,内部会找到其起因异常加入异常数组)、是否标注该注解、有则执行映射方法。
关于Tomcat异常处理:定义一个ErrorPageRegistrar的bean,重写该bean的registerErrorPages,为该ErrorPageRegistry参数修改默认错误地址addErrorPages,其使用的是请求转发的形式,然后手写一个Servlet或静态或Controller。由于不会主动调该bean的方法,还需要添加一个beanPostProcessor的bean为ErrorPageRegistrarBeanPostProcessor。另外,Tomcat会将异常对象信息存到request域里。
注意:在springBoot里有一个BasicErrorController类,其基本完成了上面手写的控制器的默认逻辑,只需将其加入到容器bean里就行,若要配置相关设置,则可以通过配置文件也可以通过其有参构造的ErrorProperties参数直接修改。但是如果使用浏览器方式访问,需要一个View的bean,除此之外还需要一个根据bean的名字对应到需要的视图上。
拦截器
概念:拦截器(Interceptor)是一种动态拦截方法调用的机制,再SpringMVC种动态拦截控制器方法的执行。
作用:再指定的方法调用前后执行预先预定的代码;阻止原始方法的执行。
具体实现:定义拦截器bean,实现HandlerInterceptor接口,其三个方法中参数handler对象封装了反射的Method对象。为SpringMvc容器添加拦截器配置,继承WebMvcConfigurationSupport,重写其addInterceptor方法,其中调用registry.addInterceptor.addPathPatterns。
拦截器链:
容器的内置Tomcat
MVC管理Tomcat项目具体实现:使用AnnotationConfigServletWebServerApplicationContext创建容器。在配置类中定义内嵌web容器工厂bean(TomcatServletWebServerFactory),创建DispatcherServlet的bean,定义注册DispatcherServlet的 bean(DispatcherServletRegistrationBean)
内部原理:在Servlet技术3.0版本以后,不再只支持web.xml管理Tomcat,支持编程管理Tomcat服务器,从而内部能够将spring容器的bean与tomcat关联起来。
注意:DispatcherServlet创建由spring创建,初始化由Tomcat处理,默认当第一次请求时进行初始化,初始化相关组件(如果没有自定义组件,某些组件有默认组件,但会作为DispatcherServlet的成员变量,而不会放在容器中),如果想要让它在Tomcat启动时就初始化,则在该DispatcherServletRegistrationBean定义里使用该对象的setLoadOnStartUp(>0则在启动时初始化)。
RequestMappingHandlerMapping组件:定义RequestMappingHandlerMapping的bean, 该bean有如下方法getHandlerMethods(获得映射结果,通过该map再有getHandler方法获得处理器执行链对象,执行链囊括拦截器,其中可以使用MockHttpServletRequest进行测试使用)。
RequestMappingHandlerAdapter组件:定义RequestMappingHandlerAdapter的bean,由于该bean的invokeHandlerMethod方法是受保护的,所以定义一个子类,就可以使用子类执行该方法调用控制器方法。另外该组件还有解析的功能,包含多种参数解析器(getArgumentResolver)、返回值解析器(getReturnValueResolver),其中要实现参数解析器:实现HandlerMethodArgumentResolver接口,重写其两个方法supportsParameter(是否支持某个参数)、resolveArgument(解析参数),最后在RequestMappingHandlerAdapter加入(setCustomArgumentResovlers)该解析器。实现返回值解析器思路相同,值得注意的是其handlerReturnValue方法参数中NativeWebRequest既有请求对象也有响应对象,通过该对象的getNativeResponse方法获得,另外默认流程在返回值处理完毕后,springMVC还会进行视图解析,使用ModelAndViewContainer参数的setRequestHandled方法设置请求已经处理完毕。参数解析流程:获得HandlerMethod映射(可以自行封装,也可以使用RequestMappingHandlerMapping组件),创建一个ModelAndViewContainer对象存储所有模型中间产生的数据,创建参数解析器(其有参构造方法的两个参数,一个beanFactory用来读取环境变量,一个表示解析是否必需该注解),创建一个ServletRequestDataBinderFactory(用来数据绑定对象和类型转换)通过遍历该映射,先判断参数解析器是否支持解析该参数,接着调用解析器的resolveArgument方法(需要注意的是其需要ModelAndViewContainer和一个binderFactory)执行解析。在多个解析器的情况下,使用组合模式,将调用解析器方法变为调用组合的解析器HandlerMethodArguementResolverComposite,使用其方法addResolvers添加解析器。
关于参数名:spring中解析名接口为ParameterNameDiscoverer。
BeanNameUrlHandlerMapping(根据bean的名字处理请求,bean的名字需要以/打头):内部原理实现HandlerMapping接口,使用@PostConstruct注解,做一个初始化方法,初始化时收集容器里所有以/打头的bean,然后再重写其getHandler方法,根据url匹配收集的集合里的bean,最后将控制器包装为HandlerExecutionChain。
SimpleControllerHandlerAdapter(处理所有实现Controller接口的控制器):内部原理是实现HandlerAdapter接口,重写其三个方法(getLastModified方法过时,处理304请求,返回-1即可),supports方法匹配所有执行实现了Controller接口的handler(handler instanceof Controller),handle方法里将handler转为Controller接口后回调接口的handleRequest方法(如果返回值是null,则不会走视图渲染流程)。
RouterFunctionMapping(配合RouterFunction<ServerResponse>使用)
RouterFunction<ServerResponse>(处理器,该bean封装了请求路径映射和处理器逻辑)
HandlerFunctionAdapter(配合RouterFunction<ServerResponse>使用)
SimpleUrlHandlerMapping(配合ResourceHttpRequestHandler使用):没有afterProperties方法,所以对应关系要手动设置(从形参注入ApplicationContext,从容器中拿到ResourceHttpRequestHandler的bean得到映射map,然后为SimpleUrlHandlerMapping设置map即setUrlMap方法)
ResourceHttpRequestHandler(处理器处理静态资源):setLocations方法设置处理路径(可以设置目录),bean的名字用通配符设置,例根路径下使用/**。另外可以设置资源解析器setRourceResolvers,常见解析器CachingResourceResolver(cache功能)、EncodedResourceResovler(解析压缩文件,压缩文件需自己生成)、PathResourceResolver(读取磁盘文件功能)。
HttpRequestHandlerAdapter(配合ResourceHttpRequestHandler使用)
WelcomePageHandlerMapping(欢迎页映射器,内置处理器不执行逻辑只转发,springBoot具备,springMvc无,配合SimpleControllerHandlerAdapter和上面静态资源处理三件套使用):为其指定参数,指定资源、映射路径、容器,第一个参数与动态控制器有关,静态资源设置为null。
关于Java
javac 编译命令
javac -parameters (默认情况下编译class文件不会保存参数名,使用此参数后将参数名保存到class文件中,存储在MethodParameters表中,可以反射获得)
javac -g(保存参数名到class文件,存储在localeVariableTable表中,不能反射获得,使用asm工具可以获得,spring可以使用基于asm的LocalVariableTableParameterNameDiscoverer工具类。并且不支持接口类,无效)
javap 反编译
java version 查看版本