springmvc知识
一、Spring MVC基础
Spring MVC简介
基于java的实现MVC设计模式的请求驱动类型的轻量级Web框架,通过注解,无需实现任何接口,处理请求,支持restful。
三层结构:表现层、业务层、持久层
设计模式:Model(模型)、View(视图)、Controller(控制器)
Spring Web MVC工作流程
SpringMvc请求处理流程:
\1. 前端用户发送请求到前端控制器DispatcherServlet
\2. DispatcherServlet收到请求之后调用处理映射器HandlerMapping
\3. 处理映射器根据url找到对应的后端控制器handler,生成处理器对象和处理器拦截器返回前端控制器
\4. 前端控制器再去调用处理适配器HandlerAdapter去调用具体的后端控制器
\5. 后端控制器执行完毕之后,把ModelAndView返回给处理适配器
\6. 处理适配器把ModelAndView返回给前端控制器。
\7. 前端控制器调用视图解析器ViewResolver进行解析,解析完毕返回视图view
\8. 前端控制器进行视图渲染,就是数据填充
\9. 前端控制器向用户响应结果
SpringMvc九大组件:
HandlerMapping(处理器映射器)、
HandlerAdapter(处理器适配器)、
HandlerExceptionResolver(异常处理)、
ViewResolver(视图解析器)、
RequestToViewNameTranslator(请求在查找视图名称,视图名转化器组件)
LocaleResolver(区域化解析,表示所在地)、ThemeResolver(主题解析)
MultipartResolver(多部件解析器文件上传处理)、FlashMapManager(flash属性管理组件 重定向参数传递)
web.xml中url-pattern配置三种方式:
方式一:带后缀,比如*.action *.do *.aaa
方式二:/ 不会拦截 .jsp,但是会拦截.html等静态资源(静态资源:除了servlet和jsp之外的js、css、png等)
因为tomcat容器中有一个web.xml(父),你的项目中也有一个web.xml(子),是一个继承关系父web.xml中有一个DefaultServlet, url-pattern 是一个 /此时我们自己的web.xml中也配置了一个 / ,覆写了父web.xml的配置
为什么不拦截.jsp呢?
因为父web.xml中有一个JspServlet,这个servlet拦截.jsp文件,而我们并没有覆写这个配置,所以springmvc此时不拦截jsp,jsp的处理交给了tomcat
方式三:/* 拦截所有,包括.jsp
数据输入类型ModelMap、Model、Map
运行的时候都会使用BindingAwareModelMap类型,而这个类型是上面三种接口的实现
Spring MVC请求参数绑定
前后端交互传输数据都是http,简称超文本传输协议。SpringMvc对serlvet封装,接收整形参数时直接在Handler⽅法中声明形参。
简单数据类型:⼋种基本数据类型及其包装类型
参数类型推荐使⽤包装数据类型,因为基础数据类型不可以为null
整型:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
说明:对于布尔类型的参数,请求的参数值为true或false。或者1或0
注意:绑定简单数据类型参数,只需要直接声明形参即可(形参参数名和传递的参数名要保持⼀致,建议 使⽤包装类型,当形参参数名和传递参数名不⼀致时可以使⽤@RequestParam注解进⾏⼿动映射)
自定义类型,比如日期处理,需要实现Converter接口,并注册自定义转换器
Restful请求支持
Restful 是⼀种 web 软件架构⻛格,它不是标准也不是协议,它倡导的是⼀个资源定位及资源操作的⻛格。
优点:
结构清晰、符合标准、易于理解、扩展⽅便
特性:
⽹络上的⼀个实体,或者说是⽹络上的⼀个具体信息。它可以是⼀段⽂本、⼀张图⽚、⼀⾸歌曲、⼀种服务,总之就是⼀个具体的存在。可以⽤⼀个 URI(统⼀资源定位符)指向它,每种资源对应⼀个特定的 URI 。要获取这个资源,访问它的 URI 就可以,因此URI 即为每⼀个资源的独⼀⽆⼆的识别符。
乱码过滤器web.xml配置,如果是put和delete请求需要配置请求方式转换,但是此方式不常用
Ajax Json交互
Json是⼀种与语⾔⽆关的数据交互格式,就是⼀种字符串,只是⽤特殊符号{}内表示对象、[]内表示数组、””内是属性或值、:表示后者是前者的值。
{“name”: “Michael”}可以理解为是⼀个包含name为Michael的对象
[{“name”: “Michael”},{“name”: “Jerry”}]就表示包含两个对象的数组
前端到后台:前端ajax发送json格式字符串,后台直接接收为pojo参数,使⽤注解@RequstBody
后台到前端:后台直接返回pojo对象,前端直接接收为json对象或者字符串,使⽤注解@ResponseBody,此注解不走视图解析器,而是直接将数据写入流中。
Spring高级技术之监听器、拦截器、过滤器
过滤器(Filter):对Request请求起到过滤的作⽤,作⽤在Servlet之前,如果url-pattern配置为/*可以对所有的资源访问(servlet、js/css静态资源等)进⾏过滤处理
监听器(Listener):实现了javax.servlet.ServletContextListener 接⼝的服务器端组件,它随Web应⽤的启动⽽启动,只初始化⼀次,然后会⼀直运⾏监视,随Web应⽤的停⽌⽽销毁,做一些初始化工作以及监听特定事件对其处理,比如从A方法进入B方法,A方法需要知道B方法执行完继续进行下一步,那么A方法就可以监听B方法的运行情况。
拦截器(Interceptor):拦截访问的控制器⽅法(Handler),一个拦截器拦截三次。
\1. 在Handler业务逻辑执⾏之前拦截⼀次
\2. 在Handler逻辑执⾏完毕但未跳转⻚⾯之前拦截⼀次
\3. 在跳转⻚⾯之后拦截⼀次
拦截器执行流程:多个拦截器执行顺序是按照配置文件拦截器的前后顺序执行的,按照先进后出的顺序,第一个拦截器先进去最后出来。
\1. 程序先执⾏preHandle()⽅法,如果该⽅法的返回值为true,则程序会继续向下执⾏处理器中的⽅法,否则将不再向下执⾏。
\2. 在业务处理器(即控制器Controller类)处理完请求后,会执⾏postHandle()⽅法,然后会通过DispatcherServlet向客户端返回响应。
\3. 在DispatcherServlet处理完请求后,才会执⾏afterCompletion()⽅法。
multipart形式(文件上传)数据、异常捕获、重定向参数传递
文件上传
客户端:POST请求;enctype=multipart;file组件
服务端: 重命名uuid;存储到磁盘,按日期建文件夹;把文件储存路径存入数据库,方便根据路径获取想要的资源。
异常捕获
需要实现HandlerExceptionResolver接口把程序所有或者单一异常都捕获做统一返回处理,比如专门做一个错误页面告知用户,给用户更好的反馈和体验。单类捕获就写在当前类就行了,全局捕获自定义一个类添加@ControllerAdvice注解。
重定向参数传递
重定向时请求参数会丢失,我们往往需要重新携带请求参数,在路径中手动拼接,解决办法声明RedirectAttributes类,可以添加flash属性,框架会在session中记录该属性值,进行重定向参数传递,而且完成之后会自动被清理。
二、自定义MVC框架
MVC框架运行原理
\1. Tomcat加载web.xml,前端控制器DispatcherServlet加载指定的配置文件springmvc.xml
\2. 根据配置文件扫描包和注解
\3. IOC容器进行相应的Bean初始化和依赖注入维护
\4. SpringMvc相关组件的初始化,建立url和method之间的映射关系,也就是处理映射器HandlerMapping
\5. 等待请求进来,处理请求。
自定义框架实现步骤
\1. 加载配置文件springmvc.properties,把文件加载成字节流然后转化成字符流存入properties。
\2. 从properties中取出配置的地址,拼接成绝对路径,扫描该路径下所有文件,找到class文件对应集合中
\3. 初始化Bean对象,变量全限定类名集合中每一个元素,进行注解判断,如果存在注解,进行反射实例化,然后存入bean对象集合中。
\4. 实现依赖注入,遍历上面的beanMap集合,然后再对遍历出来的类,取出所有属性再遍历,判断属性上面是否存在@Autowired注解,存在再进行相应处理。
\5. 构造一个HandlerMapping处理映射器,将配置好的url和method建立映射关系,遍历beanMap集合,判断类和方法上面是否存在@Controller注解,如果存在取出两个值拼接成url存入一个自定义的普通类中,类里面有所属类、方法、url以及参数位置四个属性。
\6. 执行dopost方法,接受前端请求,执行自定义MVC框架,从handler中取出参数,进行请求处理,进行参数绑定最后调用该handler所属方法的invoke方法,执行到控制层。
三、Spring MVC源码剖析
前端控制器DispatcherServlet继承结构
前端控制配置在web.xml中,执行前后端交互的重要组件,比如接收请求,调用处理器,渲染视图等等,他的继承结构如下:DispatcherServlet extends FrameworkServlet extends HttpServletBean extends HttpServlet。
\1. HttpServlet里面定义两个方法doGet、doPost方法用于获取协议等等参数,但是并没有进行具体的业务逻辑处理
\2. 在继承类FrameworkServlet中,重写了doGet、doPost方法,在这两个方法内对前端发送的请求调用自己内部定义的processRequest方法进行处理。
\3. 在processRequest方法结束调用了一个doSercive方法,他是一个抽象方法,交给了最底层类DispatcherServlet来实现。在方法内进行参数存储,设置属性值以及重定向参数管理,结尾又调用了自己内部的doDispatch方法进行请求派发。
\4. 在doDispatch方法内主要是检查是否是文件上传请求,获取处理当前请求的处理器handler,再获取处理请求的适配器,处理请求返回视图对象,最后对视图对象进行处理。
完整走向图
SpringMVC请求处理大致走向流程
\1. 前端用户发送请求,执行到控制层
\2. 接着走doGet/doPost请求,跟上面DispatcherServlet继承结构走向一致,进入到doDispatch方法开始真正的执行逻辑处理。doDispatch方法核心步骤:
1)调取getHandler()获取到能够处理当前请求的执行链HandlerExecutionChain
2)调用getHandlerAdapter方法获取能够执行1)的Handler的适配器
3)适配器调用Handler执行ha.handle,返回一个视图ModelAndView对象
4)调用processDispatchResult方法完成视图渲染调转
\3. getHandler方法剖析
如何获取适配器Handler对象?
在容器启动初始化过程中,扫描@RequestMapping注解建立url和Handler方法的对应映射关系,handlerMappings是一个接口,里面有两个实现类,通过遍历获取requestHandlerMapping实现类,通过传进来的请求request进行筛选,得到匹配的handlder并返回。
\4. getHandlerAdapter方法剖析
如何获取处理器getHandlerAdapter对象
HandlerInterceptor也是一个接口,里面有三个实现类,对不同方式请求进行分别处理,遍历获取能处理当前handler的处理器,通过supports方法内是否是这个接口的实现就能判断是否是他的处理器,返回是否处理的布尔值结果。
SpringMVC九大组件初始化
九大组件,全部定义在DispatcherServlet中,他们都定义成了接口,接口定义了规范。
Spring容器启动时,AbstractApplicationContext类中的核心方法refresh中调用了onRefresh方法,由子类实现了DispatcherServlet做了实现(完成九大件初始化),通过onRefresh方法内调用initStrategies方法完成.
多部件解析的id是固定multipartResolver,因为内部定义了一个常量就叫multipartResolver
Handler方法执行细节剖析
此方法就是上面doDispatch方法核心步骤第三步,调用ha.handle方法,针对他内部实现进行剖析。根据外部这个方法进入handleInternal方法,创建一个视图,然后判断这个session是否需要同步处理,因为session是线程不安全的,如果需要同步,则获取当前session对象并且一个唯一key增加synchronized关键字进行锁定保证线程安全。在锁里面调用invokeHandlerMethod方法对handler进行适配,如果不需要同步处理则直接进入此方法。在该方法内部进行一系列检查和前置处理,走到invokeAndHandle方法这里,开始对请求参数进行处理,调用目标的HandlerMethod,将返回值封装返回成一个ModelAndView对象。
在invokeAndHandle方法内部又请求了自己类的invokeForRequest方法,,把参数和请求以及类传递下去调用doInvoke方法使用反射对目标方法调用,并且一个Object来接收并返回。
接着回到invokeHandlerMethod方法,进行下一步,调用getModelAndView方法对上面获取的ModelAndView对象进行处理。
视图渲染细节剖析
此方法就是上面doDispatch方法核心步骤第四步,调用processDispatchResult方法,针对他内部实现进行剖析。
进入方法内部,进行异常判断和非空判断走到render方法进行渲染
在方法内部定义了一个视图对象,从getViewName方法中取出视图名称,比如success,如果不为空根据视图名进行内部视图封装方法。
在方法内部用视图解析器viewResolver进行封装。在内部的buildView方法根据配置的视图解析器在内部进行组装完整视图名称。
当完成一系列数据填充后,我们的modelMap中就有了对应数据,这时候他会把里面的数据暴露到request域中,所以当model.add完成之后,jsp就可以直接从请求域中获取数据的原因。完成数据暴露之后就调用RequestDispatcher进行请求分发,根据前面拼接的jsp完整路径调转到对应页面。
注意:转发和重定向都会走不同的视图解析,并且视图解析是走缓存的,缓存存在就直接返回,没有才重新创建。
四、Spring + Spring MVC + Mybatis整合
整合目标
数据库连接池以及事务管理都交给Spring容器来完成
SqlSessionFactory对象应该放到Spring容器中作为单例对象管理
Mapper动态代理对象交给Spring管理,我们从Spring容器中直接获得Mapper的代理对象
把SpringMVC的⼊⻔案例整合进来
所需jar包
Junit测试jar(4.12版本)
Mybatis的jar(3.4.5)
Spring相关jar(spring-context、spring-test、spring-jdbc、spring-tx、spring-aop、
aspectjweaver)
Mybatis/Spring整合包jar(mybatis-spring-xx.jar)
Mysql数据库驱动jar
Druid数据库连接池的jar
完整整合项目截图
具体引入jir包
applicationContext.xml配置
<context:component-scan base-package=”com.lg”/>
<context:property-placeholder location=”classpath:jdbc.properties”/>
<tx:annotation-driven transaction-manager=”transactionManager”>
springmvc.xml配置
<context:component-scan base-package=”com.lg.controller”>
<mvc:annotation-driven conversion-service=”conversionServiceBean”>
<mvc:resources location=”/WEB-INF/js/“ mapping=”/js/**”/>
<mvc:mapping path=”/**”/>