Sping MVC不使用任何注解处理(jQuery)Ajax请求(基于XML配置)
- 2019 年 11 月 3 日
- 筆記
1. Spring
Spring框架是一个轻量级的解决方案,是一个潜在的一站式商店,用于构建企业就绪的应用程序。Spring框架是一个Java平台,为开发Java应用程序提供全面的基础架构支持。Spring处理基础结构,因此您可以专注于应用程序。Spring使您能够从“普通的Java对象”(POJO)构建应用程序,并将企业服务非侵入性地应用于POJO。此功能适用于Java SE编程模型以及全部和部分Java EE。但是,Spring是模块化的,允许您仅使用所需的那些部分,而不必引入其余部分。您可以将IoC容器与顶部的任何Web框架一起使用,但也可以仅使用 Hibernate集成代码或JDBC抽象层。Spring框架支持声明式事务管理,通过RMI或Web服务对逻辑的远程访问以及用于持久化数据的各种选项。它提供了功能全面的MVC框架,并使您能够将AOP透明地集成到软件中。Spring被设计为非侵入式的,这意味着您的域逻辑代码通常不依赖于框架本身。在您的集成层(例如数据访问层)中,将存在对数据访问技术和Spring库的某些依赖关系。但是,将这些依赖项与其余代码库隔离起来应该很容易。Spring的两大核心特征:IoC(控制反转),AOP(面向切面编程)。IoC作用:把对象的控制权交给容器管理。AOP作用:面向切面编程(比如日志打印),底层使用动态代理实现。Spring框架包含组织为约20个模块的功能。这些模块分为核心容器,数据访问/集成,Web,AOP(面向方面的编程),检测,消息传递和测试。Spring的整个完整框 架来说,其设计原则则是”对扩展开放,对修改闭合”(OOP设计原则)。当然Spring还有很多强大的功能,这里先简单介绍一下,点到为止。
2. Spring MVC
Spring MVC是基于Servlet API构建的原始web框架,从一开始就已经包含在就包含在Spring框架中,与Spring框架无缝对接。全称应该为Spring Web MVC,其来源于模块spring-webmvc。但是,通常我们叫它Spring MVC(习惯)。Spring MVC框架是围绕一个 DispatcherServlet (核心控制器)来设计的,这个 Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区 与主题渲染等,甚至还能支持文件上传。”对扩展开放”是Spring Web MVC框架一个重要的设计原则。Spring Web MVC核心类库中的一些方法被定义为 final 方法。Sp’ring MVC的数据绑定非常灵活方便,视图解析也设计的非常灵活与方便,并且提供了好多功能强大的注解机制。当然,Spring MVC的强大之处不是一两句话可以搞定的,我们应该参考其文档,并且深入学习钻研,最好研究研究源码。这里话不多说,点到为止。
3. Spring MVC处理(jQuery)Ajax请求(前台不发送数据,后台返回普通字符串)
开发环境:Eclipse+Tomcat+Spring MVC+Jackson+JSP+jQuery+Ajax
本篇博客比较长,原本是一个小的demo,写这么长不值,不,我认为值,因为我感觉自己写的还可以(哈哈!装一波),这些都是笔者一字一句写的,当然有参考一些东西(本人最讨厌复制粘贴),加上自己的理解,涉及到代码完全认真实践过,自己从头到尾写一遍,也是对自己知识的积累以及经验的提升,Spring MVC文档和Spring文档等,请大家认真看下去,看完这篇,你不仅掌握了Ajax,认识了Spring的好处,还会熟知Spring MVC的执行流程,还会接触到有趣的日志打印。当然也可以免费获取jar包(有源码链接),百度都有(基本都免费)。所以普通的一些jar包都是免费的,没有必要花一些代价去活得开发包。本质来说,框架只是个模板,我们遵从框架的规范来开发就行了,毕竟使用一些框架是我们的开发变得简单高效,笔者认为还是应该了解一下底层核心的原理比较好,这样便于在我们开发的时候遇到bug的时候可以迅速做出决策和解决。写博客,我是认真的。
(1)搭建环境
Eclipse中新建Java Web项目,并把项目部署到Tomcat容器中。下面是项目的结构:
项目基本结构很简单,没啥可说的。这里说一下lib里面的jar包。既然我们想使用Spring MVC进行开发,必须导入其开发包。上面说了,Spring MVC其实是集成在Spring中的,所以也是导入Spring开发包。本次使用spring-framework-4.0.0.RELEASE开发包。
Spring开发包:spring-aop面向切面编程所用到的包。spring-aspects提供对AspectJ(面向切面的一个框架)的支持,spring-beans包含访问配置文件,创建和管理Bean以及进行控制反转和依赖注入操作相关的所有类。spring-core是Spring的核心包(核心工具类)。spring-expression是Spring的表达式语言,spring-jdbc它包含了spring 与 JDBC 数据访问时进行封装的所有类,提供使用springjdbc的最佳实现(利用jdbc template)。spring-orm是Spring对DAO特性进行扩展,支持一些ORM(对象关系映射)框架(比如MyBatis和Hibernate等)。spring-test提供了对Junit等测试框架的简单封装,这让我们在对Spring的代码进行测试时更加方便和快捷。spring-tx包为JDBC、Hibernate、JDO、JPA等提供了一致的声明式的编程式事物管理。spring-web包含web应用研发时用到Spring框架时所需要的的核心类。spring-webmvc包含了Spring webmvc框架相关的所有类。
log4j日志信息包。不导这个包的话会报错。
Jasckson三个包(前几篇博客说过好多遍)。这里导入Jackson包的原因说一下。1. Spring MVC内置的json与对象转换器依赖于Jackson类库。(底层通过对Jackson的一些方法进行封装实现)。2. 简单好用我感觉,效率也还可以。当然也可以实现自己的json与对象的转换器(题外话)。
com.springsource三个jar包,主要是为了提供Spring对Apache的一些服务的支持。Tomcat就是Apache的,不导这3个包的话会报错。当然,这里只是导入了spring的一部分常用的包,还有其他的(自己百度下spring开发包,在里面找找),本次开发也没把这些spring包全部用到,主要是为了着重介绍一下。好了,jar包说完了。下面进行开发。这里不使用任何注解的目的说一下,只是基于XML配置实现。其实我们使用注解会简单方便很多(后面会有更新的),此篇文章主要是为了便于大家熟悉Spring MVC的执行流程,并使用它最最原始的一些配置以及方法。方便大家了解更深层次的东西,以及深入了解Spring MVC一些核心的东西。因为我们天天使用注解,但是估计我们都没了解过注解的机制以及原理,注解有什么用。以及注解代替了那些我们之前开发比较繁杂的操作以及方法。有时候我们研究一些东西,不能只停留在表面会使用就行了,我们深入到底层的话会有意想不到的收获及理解。
(2)编写jsp文件
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html> <html> <head> <base href="<%=basePath%>"> <title>I LOVE YOU</title> <link rel="stylesheet" type="text/css" href=""> <script type="text/javascript" src="index.js"></script> <script type="text/javascript" src="jquery-3.2.1.min.js"></script> </head> <body> <button id="mybutton1" value="springmvc处理ajax请求" onclick="fun1()" >(jquery)ajax请求(不发送数据)</button> <spand id="show1" /> <br/> <hr/> </body>
很简单,定义了一个按钮。并调用js文件相关的函数进行处理,<span>标签是为了显示内容。页面引入自定义的js文件和jQeury的js文件(注意名称和路径)。
(3)编写js文件
/** * */ //使用jquery提交ajax请求(不携带数据) function fun1(){ $.ajax({ type:"POST", //发送方式 url:"UserController1", //请求地址 data:"", //数据为空 success:function(data){ //成功后的回调函数 $("#show1").html(data); //页面展示内容 } }); }
很简单,就写了一个fun1()函数。用来响应按钮。虽然说,讨论客户端不发送数据没多大意义,这里我们还是讨论一下吧。在学习java基础的时候,我们还经常讨论”空”呢。不是吗?这里主要是为了熟悉一下代码执行的流程,毕竟我们学习的过程是由浅入深的。闲话少叙。
(4)编写controller类
package com.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.HttpRequestHandler; public class UserController1 implements HttpRequestHandler{ @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setCharacterEncoding("UTF-8"); String str = "我是一个 springmvc"; try { response.getWriter().print(str); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
这里定义了一个controller类,并且实现了HttpRequestHandler接口,并实现接口中的抽象方法。方法中定义了一个字符串,利用response获取打印流进行输出即可。Spring MVC中。一般在我们编写controller(也就是一个handler,用来处理客户端的请求),要能被Sping MVC识别它是一个controller类,因为这里没有用到注解。所以我们有2种方法。
第一种:编写controller类,实现org.springframework.web.servlet.mvc.Controller接口,然后交给核心控制器调用相对应的处理器适配器进行处理。不过,实现了Controller,就必须实现其中的抽象方法public abstract ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)。这个方法必须返回一个ModelAndView对象,而我们所作的是对字符串数据和json数据的处理。所以在这里我们并没有选择实现Controller接口。
第二种:编写controller类,实现org.springframework.web.HttpRequestHandler接口,然后交给核心控制器调用相应的处理器适配器进行处理,并实现其中的抽象方法。自己可以百度一下Spring MVC的执行流程,这里不多说。
public abstract void handleRequest(HttpServletRequest request, HttpServletResponse response) 我们注意到,这个方法是没有返回值的,所以就方便了我们对字符串数据和json数据的处理。注意到有2个参数request和response(这是Servlet的东西)。Spring MVC支持的支持的参数类型:
HttpServletRequest 对象,HttpServletResponse 对象,HttpSession 对象,Model/ModelMap 对象。这里我们用到了其中的2个。
(5)配置web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>MySpringMVCAjax</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 配置springmvc核心控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 对静态资源进行放行 --> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/static/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <!-- 配置编码过滤器 --> <filter> <filter-name>SpringCharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>SpringCharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
这里我们配置了三个东西。
第一个,配置Spring核心控制器。Sping MVC核心控制器(前端控制器或中央处理器)org.springframework.web.servlet.DispatcherServlet,DispatcherServlet 其实就是个 Servlet (它继承自 HttpServlet 基类),同样也需要在你 web应用的 web.xml 配置文件下声明。你需要在 web.xml 文件中把你希望 DispatcherServlet 处理的请求映射到对应的URL上去。这就是标准的Java EE Servlet配 置。DispatcherServlet就是一个Sping MVC处理请求的一个中央处理器,负责调度处理器映射器,处理器适配器以及视图解析器。配置初始化参数来加载Sping MVC的主配置文件springmvc.xml,这里我们把它放在src目录下。
第二个,给静态资源放行。这里注意一下。我们在核心控制器<url-pattern>标签下配置的是 / ,表示所有访问的url都交给核心控制器去处理,这样导致有可能加载不到静态资源文件(js,css,img)。解决办法:
第一种:在springmvc.xml文件中配置<mvc:default-servlet-handler /><!– 表示对所有静态资源进行放行 –>(比较高级的方法)
第二种:对静态资源进行放行,如我们web.xml所示。当然,也可以在spring.xml文件进行放行,语法为:
<resources location="/js/" mapping="/js/**" /> <resources location="/css/" mapping="/css/**" /> <resources location="/images/" mapping="/images/**" />
这里使用web.xml对静态资源进行放行,主要是因为这个方法简单易懂。后面我会对博客内容进行更新升级,再采取比较高级的方法。
第三种:配置核心控制器时,把<url-pattern>标签设置为 *.do 或者 .action就好了。表示以.do结尾的或者以.action结尾的URL都由前端控制器DispatcherServlet来解析。这种方法也可以,但是到时访问路径的时候要加上这个后缀,看起来不太舒服(有一点强迫症的人应该懂)。当然笔者估计没有吧。笔者只是采取一些简单说不上高效的方法。
第三个,就是配置spring提供编码过滤器。web.xml配置的编码过滤器为了防止前端传入的中文数据出现乱码问题,使用Spring提供的编码过滤器来统一编码。配置为UTF-8。当然也可以使用GBK等其他编码。GBK编码方式的编码是以中国国情而创造的,在国际上的兼容性不好,这也是为什么大多数的网页是使用UTF-8编码而不是GBK。UTF-8好处:兼容ASCII,存储英文文件都是单字节,文件小。在eclipse中把项目的编码也设置为UTF-8,JSP页面中字符编码也设置为UTF-8。浏览器网页字符编码也设置为UTF-8。反正就是统统UTF-8的样子。做这么多主要是统一编码,解决中文乱码乱码问题。因为以前使用servlet和struts2的时候,经常出现中文乱码问题,让人摸不着什么头脑。所以。Spring提供的这个编码过滤器就比较方便好用了。所以说Spring是一个神奇的东西。我们研读一下Spring源码,看看Spring开发的文档,就可以汲取一些精华(精髓)。Spring提供的编码过滤器,org.springframework.web.filter.CharacterEncodingFilter类,注意把路径写正确。初始化参数(也就是编码)encoding为UTF-8。Spring里的字符过滤器CharacterEncodingFilter是针对请求的,forceEncoding=true是意思是指无论客户端请求是否包含了编码,都用过滤器里的编码来解析请求。forceEncoding默认为false。forceEncoding为true效果: request.setCharacterEncoding(“UTF-8”);forceEncoding为false的效果:
request.setCharacterEncoding(“UTF-8”); response.setCharacterEncoding(“UTF-8”)。 <url-pattern>标签配置的/*代表过滤所有请求,从而将编码设置为UTF-8。好了。web.xml到此结束。其实有时候我们只是开发一个简单的小例子demo,看起来很简单。但是我们要了解底层到底是怎么实现的。比如Java的底层,JVM(Java虚拟机)用c/c++编写,Java类库用Java语言编写。还有比如native本地方法,其底层本质也是c/c++编写的,Java只是调用了它的接口。这也就解释了为什么native方法效率一般比较高。那么问题来了,c语言用什么编写的 ? 这个可以自行谷歌或百度,这里不多说。当然,一个项目或者工作的执行流程我们也必须熟知。这次开发看起来很简单,但是我们把涉及到的知识内容都会为大家详细讲解,并且都是本人一字一句经过推敲实践进行编写的。当然,本人也是菜鸟一枚。知识有限。但是我们可以经过学习来获取更多的知识,提升自己的能力,从而达到人生的巅峰。不多说了(跑题了,哈哈!)。
(6)配置springmvc.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置user实体类 --> <bean id="user" class="com.pojo.User" /> <!-- 配置handler--> <bean name="/UserController1" class="com.controller.UserController1" /> <bean name="/UserController2" class="com.controller.UserController2" /> <bean name="/UserController3" class="com.controller.UserController3" /> <!-- 配置处理器映射器 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- 配置处理器适配器 --> <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/> <!-- 配置json与对象的转换器 --> <bean id="myconverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
springmvc.xml是本次开发的主配置文件。通过观察得知,顶层标签<beans>包含了许多bean模块。Spring Bean其实就是是被实例的,组装的及被Spring 容器管理的Java对象。一个bean相当于一个实体类对象,通过IoC(控制反转,也叫依赖注入)我们可以把bean注入到容器当中,交给Spring容器管理(比如bean的实例化)。这样做的目的其实解耦合。因为软件设计的原则就是高内聚,低耦合。Spring默认的bean实例是单例的,我们可以用scope属性来设置bean的作用域。scope可以接受Singleton(单例模式,每次获取bean只有一个实例)、prototype(原型模式,每次获取bean都会产生新的实例)、request(每次HTTP request请求都会产生不同的Bean实例)、session(每次HTTP session请求都会产生不同的Bean实例)、global session (每个全局的HTTPSession对应一个Bean实例。仅在portlet Context的时候才有效。)5个值。我们常用单例模式,因为创建对象本身代价比较高,耗费资源,单例的本身也满足我们的需求了。当然我们也可以根据不同的需求设置不同的scope,这本无可厚非。还有,注意一下引入的xsd文件,Spring文件中的引用的xsd文件是用于校验xml文件的格式用的。Spring默认在启动时是要加载XSD文件来验证xml文件的。相当于是约束文件。这里我们引入了三个xsd文件,spring-beans-xsd包含对bean的解释。sprng-mvc.xsd包含了对<mvc>等标签的解释,spring-context.xsd包含对context的解释,这里我们其实只用到了spring-beans.xsd约束文件。
我们具体看一下配置了什么东西。
1. 把User实体类注入到容器(下面会贴User类的代码,这里暂时还没用到)。相当于是通过<bean>标签来装配一个User类,我们就可以通过获取bean来达到获取User实体对象的目的。设置bean的id(一个bean的唯一标识)和class(类的全包路径 包.类)。
2. 配置controller(也就是handler,处理客户端请求)。这里我们配置了好三个controller。我们看UserController1(对应上面的方法UserController1)的配置,配置了一个name和class。Spring MVC要识别请求的url,就是通过调用处理器映射器获取到这个name的名称,找到controller类,然后交给处理器设配器去处理controller。相当于就是映射路径。注意名称前要加上/ 不然无法识别。这里我们设置name=”/UserController1“和类名保持一致(为了方便),当然也可以起其他名字,别忘了/,前台提交的url和此处name的名称要保持一致。class为UserController1的全包类路径。
3. 配置处理器映射器BeanNameUrlHandlerMapping。不需要指定id,只配置全类路径即可,即class。这个处理器映射器,将bean的name作为url进行查找,需要在配置Handler时指定bean的 name(url)。
查看spring-webmvc包下面的DispatcherServlet.properties 资源文件:
# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping, org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter, org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter, org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver, org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver, org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
Spring MVC的处理器映射器,常用的有2个,一个是org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,继承了
AbstractDetectingUrlHandlerMapping抽象类,它又向上继承了AbstractUrlHandlerMapping抽象类,它又向上继承了AbstractHandlerMapping,
AbstractHandlerMapping抽象类实现了HandlerMapping接口。
另一个是org.springframework.web.servlet.handler.SimpleUrlHandlerMapping,继承了AbstractUrlHandlerMapping抽象类,
AbstractUrlHandlerMapping继承了AbstractHandlerMapping抽象类,AbstractHandlerMapping实现了org.springframework.web.servlet.HandlerMapping
接口。观察资源文件发现,BeanNameUrlHandlerMapping是Spring MVC的默认处理器映射器,这里我们就使用这个。若要使用SimpleUrlHandlerMapping,
我们根据它的语法来就行了。可以这样配置:
bean id="UserController1" class="com.controller.UserController1" /> <!-- 简单URL配置处理器映射器 --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/UserController1">UserController1</prop> </props> </property> </bean>
当然这两种处理器映射器配置可以并存,核心控制器会正确的去判断 url 用哪个 Handler 去处理。
注意这个上面资源文件的处理器映射器DefaultAnnotationHandlerMapping,通过查看源码,它已经被废弃了。被抛弃的感觉不好受啊。
(估计DefaultAnnotationHandlerMapping的内心是崩溃的)。
4. 配置处理器适配器。不需要指定id,class为全类路径。核心控制器调用处理器映射器找到了Controller类,那么谁来处理这个Controller呢。那么此时处理器适配器就闪亮登场了。什么是处理器适配器呢。且听下回分解,本章完。。。(皮一下)。通过观察以上资源文件。我们发现。Spring MVC的处理器适配器,常用的有2个。
一个是org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,一个是org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
这2个类都实现了HandlerAdapter接口。SimpleControllerHandlerAdapter处理的Handler必须实现Conreoller接口,一般返回一个ModelAndView对象。
HttpRequestHandlerAdapter处理的Handler必须实现HttpRequestHandler接口,一般用来处理字符串或者json数据。因为其抽象方法没有返回值。AnnotationMethodHandlerAdapter已经废弃了,不多说。当然两种适配器可以共存,配置不同的映射器找到不同的controller。
Spring MVC默认的处理器适配器是HttpRequestHandlerAdapter 。
5. 配置视图解析器。不需要指定id,class为全类路径。我们这里用的是InternalResourceViewResolver,配置它的class路径,注意有2个属性。prefix表示返回视图页面的前缀,suffix表示返回视图页面的后缀。比如一般我们要视图解析完成后返回一个页面index.jsp时,prefix相当于配置的是其根目录(在哪儿),suffix就是.jsp,这样在我们使用注解进行开发的时候,只需要返回一个字符串”index”就行了。上面那样配置,表示在 Handler 中只需要返回在 WebContent根目录下的jsp 文件名就ok了(为了简单方便,开发比较高效)。视图解析器作用它负责将一个代表逻辑视图名的字符串 (String)映射到实际的视图类型 View 上。通过以上资源文件发现。注意HandlerExceptionResolver处理器异常解析器。它负责将捕获的异常映射到不同的视 图上去,此外还支持更复杂的异常处理代码。Spring MVC的视图解析器这里介绍3种:
第一种:使用ViewResolver接口解析视图
org.springframework.web.servlet.view.InternalResourceViewResolver类,通过连续向上继承,实现org.springframework.web.servlet.ViewResolver接口。在实际应用中InternalResourceViewResolver也是使用的最广泛的一个视图解析器。本次开发就用的是这个视图解析器。这个比较常用。InternalResourceViewResolver解析器可以解释为内部资源视图解析器。InternalResourceViewResolver会把返回的视图名称都解析为InternalResourceView对象,InternalResourceView会把Controller处理器方法返回的模型属性都存放到对应的request属性中,然后通过RequestDispatcher在服务器端把请求forword重定向到目标URL。
接口中的方法:通过传来的参数解析视图,并返回一个View对象。注意Handler类实现Conteoller接口方法时,就返回了一个ModelAndView对象。ModelAndView 是SpringMVC 框架的一个底层对象,包括 Model 和 View。
public abstract interface ViewResolver
{
public abstract View resolveViewName(String paramString, Locale paramLocale)
throws Exception;
}
第二种:使用RequestToViewNameTranslator接口解析视图。DefaultRequestToViewNameTranslator为这个接口的实现子类。
这个接口定义了一个抽象方法
public abstract interface RequestToViewNameTranslator
{
public abstract String getViewName(HttpServletRequest paramHttpServletRequest)
throws Exception;
}
表示根据request请求返回一个视图名称的字符串。
第三种:使用FlashMapManager接口解析视图。SessionFlashMapManager为这个接口的实现子类。
FlashMap管理器。它能够存储并取回两次请求之间 的 FlashMap 对象。后者可用于在请求之间传递数据,通常 是在请求重定向的情境下使用。
这个接口定义的方法:自己看看就好了,在这里不做过多介绍。
public abstract interface FlashMapManager
{
public abstract FlashMap retrieveAndUpdate(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse);
public abstract void saveOutputFlashMap(FlashMap paramFlashMap, HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse);
}
Map肯定时用来保存数据的。RedirectView在页面跳转,数据的保存依赖于FlashMap和FlashMapManger,FlashMapManger在容器初始化时被填入,而FlashMap从Manger可以获取。
5. 配置json与对象的转换器。这里暂时用不到。下面会解释。先跳过。其实Spring MVC内置的json与对象的转换器底层还是用Jasckson类库实现。
(7)配置log4j.properties日志文件
### set log levels ### log4j.rootLogger = INFO , console , D ### console ### log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%p]-[%c] %m%n ### log file ### log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File =../logs/IvaDubboWeb-info.log log4j.appender.D.Append = true log4j.appender.D.Threshold = INFO log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %m%n ### out zhiding log file ### log4j.logger.haha = INFO, haha log4j.additivity.haha = false log4j.appender.haha = org.apache.log4j.DailyRollingFileAppender log4j.appender.haha.File =D:/logs/mylog.log log4j.appender.haha.Append = true log4j.appender.haha.Threshold = INFO log4j.appender.haha.layout = org.apache.log4j.PatternLayout log4j.appender.haha.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %m%n
Log4j简单介绍:
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护线程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局)。这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出。综合使用这三个组件可以轻松地记录信息的类型和级别,并可以在运行时控制日志输出的样式和位置。
1. 配置根logger,日志输出级别为INFO级别。log4j的输出级别:TRACE < DEBUG < INFO < WARN < ERROR < FATAL。比INFO级别高的也会打印输出,比INFO低的不会输出。
2. 配置console,在eclipse的控制台打印日志信息。
3. 配置输出到文件。一个是输出到一般性文件中,另一个是输出到我们指定的文件中。很简单。(笔者对log4j还琢磨了半天,主要是了解日志打印到底是什么玩意儿)。
对上面log4j简单测试一下(注意要导入log4j开发包和单元测试JUinit包,并把log4j.properties文件放在src目录下)。打印到控制台:
package com.log4j; import org.apache.log4j.Logger; import org.junit.Test; public class Log4jTest { @Test public void test() { // BasicConfigurator.configure(); //自动快速地使用缺省Log4j环境。 Logger logger = Logger.getLogger(Log4jTest.class); logger.info("log4j"); logger.info("是"); logger.error("什么"); logger.debug("呢"); } }
运行效果:debug级别比INFO低,无输出。
打印日志到指定文件:把方法放在一个类中就行了
@Test public void test1() { // BasicConfigurator.configure(); //自动快速地使用缺省Log4j环境。 Logger logger = Logger.getLogger("haha"); logger.info("我"); logger.info("要"); logger.info("学"); logger.info("java"); logger.info("哈"); logger.info("哈"); }
运行效果:
(8)运行程序
启动Tomcat服务器,在浏览器地址栏上输入:localhost/MySpringAjax/
完美运行,不做过多解释。
4. Spring MVC处理(jQuery)Ajax请求(前台发送key/value数据,后台返回json数据)
(1)编写jsp页面
重复步骤我们一笔带过就好了。比如jar包都已经导好了,还有log4j.properties文件,springmvc.xml主配置文件,这些都是项目公用的。这里就不多说了。
<button id="mybutton2" value="springmvc处理ajax请求" onclick="fun2()" >发送数据格式为key/value的(jquery)ajax请求</button> <spand id="show2" />
(2)编写js页面
//使用jquery提交key/value数据(ajax请求) function fun2(){ $.ajax({ type:"POST", url:"UserController2", data:"username=wly&password=1314520", //key/value数据 success:function(data){ $("#show2").html(data.username+" "+data.password); } }); }
(3)编写User类
package com.pojo; public class User { private String username; private String password; private Integer age; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User [username=" + username + ", password=" + password + ", age=" + age + "]"; } }
这里我们给User类增加了一个age属性,前台发送的还是username和password。(增加age属性主要是为了对User类进行重新的一些操作,就是方便我们演示不同的效果)。
(4)配置web.xml文件
上面已经配置好了,配置三个东西。1. Spring MVC核心控制器;2. 对静态资源进行放行;3. 配置Spring MVC提供的编码过滤器
(5)配置springmvc.xml文件
上面已经配置好了,需要配置的bean有,User实体类,Controller类,处理器映射器,处理器适配器,视图解析器,json与对象的转换器(本次开发就调用Spring MVC内置的json与对象的转换器进行进行json数据与对象的转换)。配置json与对象转换器的id(唯一识别这个bean)和class(全包类路径)。
<!-- 配置json与对象的转换器 --> <bean id="myconverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
这里我们看一下配置的Spring MVC内置的json与对象的转换器,这里我们使用的是MappingJackson2HttpMessageConverter类。我们先介绍一下Spring MVC的2个常用的消息转换器。
1. org.springframework.http.converter.json.MappingJackson2HttpMessageConverter消息转换器类,这个类继承了AbstractHttpMessageConverter<T>抽象类,而这个抽象类实现了package org.springframework.http.converter.HttpMessageConverter<T>接口。我们看一下这个接口的抽象方法。
package org.springframework.http.converter; import java.io.IOException; import java.util.List; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; public abstract interface HttpMessageConverter<T> { public abstract boolean canRead(Class<?> paramClass, MediaType paramMediaType); public abstract boolean canWrite(Class<?> paramClass, MediaType paramMediaType); public abstract List<MediaType> getSupportedMediaTypes(); public abstract T read(Class<? extends T> paramClass, HttpInputMessage paramHttpInputMessage) throws IOException, HttpMessageNotReadableException; public abstract void write(T paramT, MediaType paramMediaType, HttpOutputMessage paramHttpOutputMessage) throws IOException, HttpMessageNotWritableException; } /* Location: F:eclipseWorkspacemyworkspaceMySpringMVCAjaxWebContentWEB-INFlibspring-web-4.0.0.RELEASE.jar * Qualified Name: org.springframework.http.converter.HttpMessageConverter * Java Class Version: 6 (50.0) * JD-Core Version: 0.7.0.1 */
一般来说,我们在Spring MVC实现自定义的json与对象的转化器时,就应该实现HttpMessageConverter<T>这个接口。MediaType在网络协议的消息头里面叫做Content-Type,使用两部分的标识符来确定一个类型,我们用的时候其实就是为了表明我们传的东西是什么类型。MediaType类就是一个媒体类型,定义了好多静态常量,定义的就是数据的格式。相当于ContentType。静态常量值有application/json,text/html,image/png,application/xml等等。有好多呢。自己去看源码好了。canRead()方法表示检测能不能把java对象转化为json数据,canWrite表示检测能不能把json数据转化为java对象。read()方法用来读,表示把json数据转化为java对象,write方法表示写,把java对象转换为json数据输出。MappingJackson2HttpMessageConverter是HttpMessageConverter<T>的实现子类,先来看一下AbstractHttpMessageConverter<T>抽象类定义的2个重要的抽象方法。实现HttpMessageConverter<T>接口中的read()和write()抽象方法。但是调用的还是本类定义的2个抽象方法。再交给起子类去实现这2个方法。
/* */ public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage) /* */ throws IOException /* */ { /* 158 */ return readInternal(clazz, inputMessage); /* */ } /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage) /* */ throws IOException, HttpMessageNotWritableException /* */ { /* 170 */ final HttpHeaders headers = outputMessage.getHeaders(); /* 171 */ if (headers.getContentType() == null) { /* 172 */ if ((contentType == null) || (contentType.isWildcardType()) || (contentType.isWildcardSubtype())) { /* 173 */ contentType = getDefaultContentType(t); /* */ } /* 175 */ if (contentType != null) { /* 176 */ headers.setContentType(contentType); /* */ } /* */ } /* 179 */ if (headers.getContentLength() == -1L) { /* 180 */ Long contentLength = getContentLength(t, headers.getContentType()); /* 181 */ if (contentLength != null) { /* 182 */ headers.setContentLength(contentLength.longValue()); /* */ } /* */ } /* 185 */ if ((outputMessage instanceof StreamingHttpOutputMessage)) { /* 186 */ StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage)outputMessage; /* */ /* */ /* 189 */ streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() /* */ { /* */ public void writeTo(final OutputStream outputStream) throws IOException { /* 192 */ AbstractHttpMessageConverter.this.writeInternal(t, new HttpOutputMessage() /* */ { /* */ public OutputStream getBody() throws IOException { /* 195 */ return outputStream; /* */ } /* */ /* */ public HttpHeaders getHeaders() /* */ { /* 200 */ return AbstractHttpMessageConverter.1.this.val$headers; /* */ } /* */ }); /* */ } /* */ }); /* */ } /* */ else { /* 207 */ writeInternal(t, outputMessage); /* 208 */ outputMessage.getBody().flush(); /* */ } /* */ } /* */ /* */ /* */ protected abstract T readInternal(Class<? extends T> paramClass, HttpInputMessage paramHttpInputMessage) /* */ throws IOException, HttpMessageNotReadableException; /* */ /* */ protected abstract void writeInternal(T paramT, HttpOutputMessage paramHttpOutputMessage) /* */ throws IOException, HttpMessageNotWritableException; /* */ }
接下来,MappingJackson2HttpMessageConverter就出现了,其实现了抽象类定义的方法readInternal()和writeInternal,通过观察发现。readInternal()方法其调用了ObjectMapper(Jackson核心操作类)的readValue()方法,表示将json字符串转化为java对象。read()方法其实是本类扩展的一个方法,作用和readInternal一样。writeInternal()方法其调用了ObjectMapper(Jackson核心操作类)的writeValue()方法,表示将java对象转化为json字符串。
公共接口HttpInputMessage,扩展HttpMessage,表示HTTP输入消息,由表头 和可读主体组成。通常由服务器端的HTTP请求句柄或客户端的HTTP响应句柄实现。其实现子类ServletServerHttpRequest封装了请求头header和请求体body。
公共接口HttpOutputMessage ,扩展HttpMessage表示HTTP输出消息,由标头 和可写主体组成。通常由客户端的HTTP请求句柄或服务器端的HTTP响应句柄实现。其实现子类ServletServerHttpResponse相当于封装了响应头和响应体。
/* */ protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) /* */ throws IOException, HttpMessageNotReadableException /* */ { /* 168 */ JavaType javaType = getJavaType(clazz, null); /* 169 */ return readJavaType(javaType, inputMessage); /* */ } /* */ /* */ /* */ public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) /* */ throws IOException, HttpMessageNotReadableException /* */ { /* 176 */ JavaType javaType = getJavaType(type, contextClass); /* 177 */ return readJavaType(javaType, inputMessage); /* */ } /* */ /* */ private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) { /* */ try { /* 182 */ return this.objectMapper.readValue(inputMessage.getBody(), javaType); /* */ } /* */ catch (IOException ex) { /* 185 */ throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex); /* */ } /* */ } /* */ /* */ /* */ protected void writeInternal(Object object, HttpOutputMessage outputMessage) /* */ throws IOException, HttpMessageNotWritableException /* */ { /* 193 */ JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType()); /* */ /* */ /* */ /* */ /* 198 */ JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding); /* */ /* */ /* */ /* 202 */ if (this.objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) { /* 203 */ jsonGenerator.useDefaultPrettyPrinter(); /* */ } /* */ try /* */ { /* 207 */ if (this.jsonPrefix != null) { /* 208 */ jsonGenerator.writeRaw(this.jsonPrefix); /* */ } /* 210 */ this.objectMapper.writeValue(jsonGenerator, object); /* */ } /* */ catch (JsonProcessingException ex) { /* 213 */ throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex); /* */ } /* */ } /* */
2. org.springframework.http.converter.StringHttpMessageConverter消息转换器类,继承了AbstractHttpMessageConverter<String>抽象类,而这个抽象类实现了HttpMessageConverter<T>接口。主要用于以读写字符串。其实就是一个消息转换器。默认情况下,该转换器支持所有介质类型(*/*
),并用写Content-Type
的text/plain
。可以通过设置supportedMediaTypes该属性来覆盖。原理:通过输入和输出流的方式进行读和写操作。
设计哲学:StringHttpMessageConverter的哲学便是:你想要什么类型的数据,我便发送给你该类型的数据。
其实很简单的道理,比如我希望接受的数据类型是Accept: application/json;charset=UTF-8,发送的数据类型Content-Type: application/json;charset=UTF-8 当然也要保持一致。相当于以什么格式输入的字符串,就得以相应的格式进行转换。这里要深入了解的话请看具体源码。
(6)配置编写Controller类
package com.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.web.HttpRequestHandler; import com.pojo.User; public class UserController2 implements HttpRequestHandler{ public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setCharacterEncoding("UTF-8"); //加载spring.xml文件初始化ApplicationContext ApplicationContext ac = new ClassPathXmlApplicationContext("springmvc.xml"); //得到User类的实例(对象),可以看出IoC(把对象交给容器管理)机制(工厂模式+Java反射机制) User user = (User) ac.getBean("user"); //获取username和password,进行手动封装 String username=request.getParameter("username"); String password=request.getParameter("password"); user.setUsername(username); user.setPassword(password); //获取消息转换器MappingJackson2HttpMessageConverter类对象,实现了HttpMessageConverter接口 HttpMessageConverter converter= (HttpMessageConverter) ac.getBean("myconverter"); //获取媒体类型,传入的是"application/json;charset=UTF-8"这里我们直接new MediaType mediaType=new MediaType(MediaType.APPLICATION_JSON, null); //实例化响应信息类。ServletServerHttpResponse是HttpOutputMessage的实现子类 //这里直接new,要是配置到springmvc里面显得配置文件冗余。 HttpOutputMessage out=new ServletServerHttpResponse(response); converter.write(user, mediaType, out); } }
首先要获取2个bean,一个是配置的转换器,一个是User类对象。通过加载spring.xml初始化Spring的一个应用上下文ApplicationContext对象,调用方法getBean(“id”)。我们可以获得bean对象,其实这是通过Java反射机制完成的。这个传入的id是我我们spring.xml中所配置的id,注意一定要对应,保持一致。关于获取Spring Bean还有其他方法,自己可以百度。得到user对象后,手动进行封装。代码都有注解,很容易理解。MediaType.APPLICATION_JSON是一个静态常量。MediaType类中定义的这个静态常量(还有好多,这里只列举一个)public static final MediaType APPLICATION_JSON = valueOf(“application/json”)。HttpOutputMessage封装输出的信息。最后通过消息转换器的write()方法把对象转化为json数据进行输出,响应客户端。
ApplicationContext是一个中央接口,为应用程序提供配置。在应用程序运行时,它是只读的,但是如果实现支持,则可以重新加载。
ApplicationContext提供:
1. 用于访问应用程序组件的Bean工厂方法。继承自ListableBeanFactory。
2.以通用方式加载文件资源的能力。从ResourceLoader
接口继承。
3.将事件发布给注册的侦听器的能力。从ApplicationEventPublisher接口继承。
4.解决消息的能力,支持国际化。从MessageSource接口继承。
5.从父上下文继承。在后代上下文中的定义将始终优先。例如,这意味着整个Web应用程序都可以使用单个父上下文,而每个servlet都有其自己的子上下文,该子上下文独立于任何其他servlet的子上下文。
存在问题:这里的User类对象其实最后还是通过Spring容器new的实例(反射),完全不用注解的话,怎么自动完成User类对key/value类型数据的封装呢,我们怎么得到user类呢。有一种思路(深入了解spring mvc的参数绑定机制,这就要又得研究研究源码了)。不知道大家还有什么好的办法。欢迎交流哈。
(7)运行程序
5. Spring MVC处理(jQuery)Ajax请求(前台发送json数据,后台返回json数据)
(1)编写jsp页面
<button id="mybutton3" value="springmvc处理ajax请求" onclick="fun3()" >发送数据格式为json的(jquery)ajax请求</button> <spand id="show3" /><br/>
(2)编写js文件
//使用jquery提交json数据(ajax请求) function fun3(){ var user={ //符合json数据格式规范的javascript对象 "username":"我是谁", "password":"1314520" }; $.ajax({ type:"POST", url:"UserController3", contentType:"application/json;charset=UTF-8", //发送数据到服务器时所使用的内容类型 data:JSON.stringify(user), //将javascript对象转化为json字符串 //预期的服务器响应的数据类型。服务器返回json字符串。jquery会自动把json转化为js对象 dataType:"json", //相当于调用JSON.parse(data)方法。此时我们省去即可。 success:function(data){ $("#show3").html(data.username+" "+data.password+" "+data.age); } }); }
打印信息时,我们加了一个age属性注意一下。这里username设置为中文,主要为了测试会不会出现乱码(看web.xml配置的编码过滤器生效没)。
(3)编写User类
User实体类和上面一样。有3个属性,username,password,age。提供getters和setters方法,toString方法。
(4)配置web.xml
一样
(5)配置springmvc.xml文件
同理
(6)编写Controller类
package com.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.stereotype.Controller; import org.springframework.web.HttpRequestHandler; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.fasterxml.jackson.databind.ObjectMapper; import com.pojo.User; public class UserController3 implements HttpRequestHandler{ @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setCharacterEncoding("UTF-8"); ////加载spring.xml文件初始化ApplicationContext ApplicationContext ac = new ClassPathXmlApplicationContext("springmvc.xml"); ////获取消息转换器MappingJackson2HttpMessageConverter类对象,实现了HttpMessageConverter接口 HttpMessageConverter converter=(HttpMessageConverter) ac.getBean("myconverter"); ////获取媒体类型,传入的是"application/json;charset=UTF-8"这里我们直接new对象 MediaType mediaType=new MediaType(MediaType.APPLICATION_JSON, null); //请求信息类,封装输入信息 HttpInputMessage in=new ServletServerHttpRequest(request); //响应信息类,封装输出信息 HttpOutputMessage out=new ServletServerHttpResponse(response); //把前台传来传来json数据转化为User对象(read接受的参数:这里可以把json数据转化为User,得益于HttpInputMessage已经把请求的信息已经封装好了,包括请求头和请求体) User user= (User) converter.read(User.class, in); //设置年龄 user.setAge(666); System.out.println(user); //把User对象转化为json数据输出 converter.write(user, mediaType, out); } }
代码都有注解。很简单。消息转换器对象的write()方法读客户端发送的json数据,把json数据转换为User类对象,为什么要转换?(方便我们代码进行操作和维护。比如我们登陆时,要查数据库看这个user存在不,设置其他属性什么的,比如age。所以是有一定需求的。java语言是面向对象程序设计语言,肯定操作java对象方便快捷)。
(7)跑一下程序
完美运行,没有出现乱码,好了,收工。打印出了username,password,age的内容。
6. 理解Spring MVC的执行流程
(1)用户发送请求到Spring MVC核心控制器(DispatcherServlet)。
(2)前端控制器请求 HandlerMapping 查找 Controller,可以根据 xml 配置、注解进行查找。
(3) 处理器映射器 HandlerMapping 查找完成后向核心控制器返回 Controller。
(4)前端控制器调用处理器适配器去执行 Controller。
(5)处理器适配器执行 Controller。
(6)Controller执行完成后给适配器返回 ModelAndView对象。
(7)处理器适配器向前端控制器返回 ModelAndView。ModelAndView 是SpringMVC 框架的一个底层对象,包括 Model 和 View。
(8)前端控制器请求试图解析器去进行视图解析根据逻辑视图名来解析真正的视图。
(9)视图解析器向前端控制器返回 view。
(10)核心控制器进行视图渲染。就是将模型数据(在 ModelAndView 对象中)填充到 request 域
(11)核心控制器向用户响应结果。
7. 总结
(1)导包。不到log4j日志包会出现错误,Spring框架支持log4j日志输出。支持Apache服务的包也要导入(com.springresource)。Jackson包导全。Spring开发基本包。
(2)加载不到静态资源,要对静态资源文件进行放行。
(3)Spring的IoC机制,可以把对象交给容器管理。IoC核心:通过配置文件或者是Annonation的方式,将实现类注入到调用者那里去。
IoC实现原理:工厂模式+java反射机制。IOC好处:
第一:资源集中管理,实现资源的可配置和易管理。
第二:降低了使用资源双方的依赖程度,也就是我们说的耦合度。
第三:通过配置文件,可以做到改变实现类,而不改变任何一个调用者的代码(IOC)。
(4)本次开发我们使用到了Spring MVC内置的消息转换器MappingJackson2HttpMessageConverter(用于java对象与json数据的转换)。其内部实现原理还是使用到了Jackson开发包,这就是为什么要导入Jackson包的原因。当然,我们也可以直接使用Jackson类库进行java对象与json数据的转换(我的前几篇博客)。这里调用内置的消息转换器,方便大家理解。Spring MVC的执行流程,以及底层到底是怎么进行java对象与json数据的。当然也可以在Spring MVC自定义自己的消息转换器类,让其实现HttpMessageConverter<T>接口。
(5)本次开发的强大之处在于没有使用任何Spring的任何注解,基于纯XML配置,走原生模式。方便大家了解底层的实现原理。
(6)学习的时候可以多参考Spring和Spring MVC的开发文档。有不懂的类可以参考Spring API文档。
(7)通过本次开发,发现Spring MVC的优点:Spring MVC与Spring无缝对接,使用简单,易扩展,灵活性比较强,可适配,非侵入,清晰的角色划分,强大的配置方式,可定制的handler mapping和view resolver。当然我觉得Spring MVC最好的地方在于提供了非常灵活的参数绑定机制和强大的注解机制。