本篇Blog介绍另一个常用的技术Ajax。虽然Ajax可以脱离SpringMVC去使用,但是SpringMVC对AJax有更好的支持
AJAX概念概述
AJAX即Asynchronous Javascript And XML(异步JavaScript和XML),AJAX不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术。它是一套综合了多项技术的浏览器端网页开发技术。这些技术包括Javascript、XHTML和CSS、DOM、XML和XMLHttpRequest。
通过在浏览器与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新
AJAX优缺点归纳
使用AJAX的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的html代码信息。
- 减轻服务器负担,按需要获得数据。
- 无刷新更新页面,减少用户的实际和心理的等待时间。
- 更好的用户体验。
- 减轻宽带的负担。
- 主流浏览器支持。
当然缺点也比较明显:
- AJAX大量使用了Javascript和AJAX引擎,使用AJAX的程序必须测试针对各个浏览器的兼容性。
- AJAX更新页面内容的时候并没有刷新整个页面,因此,网页的后退功能是失效的。
- 对搜索引擎支持不好
索性AJAX提供了一些浏览器的兼容使用方式。
AJAX原理
AJAX的原理简单来说通过浏览器的javascript对象XmlHttpRequest(Ajax引擎)对象来向服务器发异步请求并接收服务器的响应数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。即用户的请求间接通过AJAX引擎发出而不是通过浏览器直接发出,同时Ajax引擎也接收服务器返回响应的数据,所以不会导致浏览器上的页面全部刷新,通俗的说,就是浏览器使用AJAX引擎做代理来处理请求和响应
浏览器创建ajax对象,AJAX对象向后端发送请求,后端接收请求响应数据给前端,AJAX对象接收响应数据通过dom操作更新视图,整个过程浏览器“不刷新”,真正的请求是由AJAX引擎发出。不是由浏览器窗口发出,所以浏览器窗口是不会刷新的, AJAX引擎同时也接收服务器返回的响应内容
AJAX发送请求方式
上述我们提到了AJAX引擎,AJAX引擎就是XMLHttpRequest对象,所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)。它同时也是一个Javascript对象,常用方法与属性如下:
常用属性:
使用JavaScript语法发送请求
以下是使用JavaScript语法发送请求的get请求方式的标准步骤:
//1.创建ajax对象 var xhr = new XMLHttpRequest(); //2.配置请求方式和请求地址 xhr.open("get","xxx地址"); //如果要传入参数 xhr.open("get","xxx地址?参数名1=参数值1&参数名2=参数值2") //如果要设置请求方式,true表示同步,false表示异步 xhr.open("get","xxx地址?参数名1=参数值1&参数名2=参数值2",true) //3.发送请求 xhr.send(); //4.监听状态变化&接收响应数据 xhr.onreadystatechange = function(){ if(xhr.readyState===4 && xhr.status===200){ var data = xhr.reponseText; } }
这里需要注意两点,同步异步以及事件监听机制。
同步和异步的概念
- 同步:就是在发出一个功能调用时,在没有得到结果之前,不能够继续调用其他功能。
- 异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果,程序继续向下执行。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
同步是阻塞模式,异步是非阻塞模式。发送请求的区别:
- 同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
- 异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式
AJAX中的异步请求是异步的真正的实现,是指用户页面(HTML)没有直接和服务器打交道而是通过Ajax引擎间接和服务器打交道的, 而用户页面和Ajax引擎打交道时并不会等待服务器返回的响应内容,页面中的Javascript代码继续执行,也可以继续发出新的Ajax请求
事件监听的概念
需要注意其中onreadystatechange是一个监听事件(监听ajax对象的状态(readyState)变化,整个过程中readyState状态值会不断发生改变),readyState: 一共有五个值(状态4需要记忆)
- 0 刚创建的ajax对象状态为0
- 1 调用了open方法之后
- 2 后端接收请求
- 3 后端处理数据,响应数据中,数据可能没有完全传输完
- 4 数据传输完成已就绪,放在responseText属性中。
status是状态码,标识请求结果,responseText用来存放后端响应的数据,是字符串格式。post的方式类似:
//1.创建ajax对象 var xhr = new XMLHttpRequest(); //2.配置请求方式和请求地址 xhr.open("post","xxx地址"); //如果要传入参数 xhr.open("post","xxx地址?参数名1=参数值1&参数名2=参数值2") //如果要设置请求方式,true表示同步,false表示异步 xhr.open("post","xxx地址?参数名1=参数值1&参数名2=参数值2",true) //3.设置请求头 xhr.setRequestHeader("Content-Type","application/x-www-form-encoded"); //4.发送 xhr.send(); //5.监听状态和接收数据 xhr.onreadystatechange = function(){ if(xhr.readyState===4 && xhr.status===200){ var data = xhr.responseText; } }
使用JQuery语法发送请求
上述示例是使用原生JS语法发送AJAX请求,下面这几种是JQuery语法的形式:
//1.get方式 $.get('xxx地址',{ "属性名1":"属性值1", "属性名2":"属性值2" },function(data){ //data存储就是后端响应的数据 console.log(data); }); //2.post方式 $.post('xxx地址',{ "属性名1":"属性值1", "属性名2":"属性值2" },function(data){ //data存储就是后端响应的数据 console.log(data); }); //3.万能模式 $.ajax({ url:'xxx地址', type:"请求方式", dataType:”json” , //告诉后端我们想要的数据格式 data:{ "属性名1":"属性值1", "属性名2":"属性值2" }, success:function(data){ //data存放的就是后端响应的数据 console.log(data); } })
我们一般更多的是使用JQuery的形式去实现
SpringMVC整合AJAX
我们一般使用AJAX作为表单的验证和用户信息的验证,这样提示信息就可以直接打到前端页面上。我们的目标是模拟一个用户验证,只用用户名和密码输入完全正确才允许登录。
1 新建一个项目
首先我们新建一个AJAX的SpringMVC项目
配置pom.xml
依赖,同时也引入jackson包
<!--https://mvnrepository.com/仓库获取的最新包 20210831--> <dependencies> <!-- jackson包引入--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency> <!--Spring MVC框架依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.9</version> </dependency> <!--JSP相关依赖--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--servlet相关依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--单元测试相关依赖--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> </dependencies>
2 创建Model类
我们创建一个Model类,这个Model类就是要返回给前端的VO
package com.example.ajax.model; import lombok.Data; @Data public class User { private String username; private String password; }
3 编写AJAX控制器Controller
我们需要编写一个控制器,注意控制器可以使用注解@RestController来标明所有返回的数据都将被序列化为Json格式:
package com.example.ajax.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class AjaxController { @RequestMapping("/argcheck") public String ArgCheck(String name,String pwd){ String msg = ""; //模拟数据库中存在数据 if (name!=null){ if ("admin".equals(name)){ msg = "OK"; }else { msg = "用户名输入错误"; } } if (pwd!=null){ if ("111111".equals(pwd)){ msg = "OK"; }else { msg = "密码输入有误"; } } return msg; //由于@RestController注解,将msg转成json格式返回 } }
4 注册DispatcherServlet
接着是我们的传统艺能,注册SpringMVC过滤器来拦截请求
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置前端控制器--> <servlet> <!--1.注册DispatcherServlet--> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联--> <init-param> <param-name>contextConfigLocation</param-name> <!--关联一个springMVC的配置文件:【servlet-name】-servlet.xml--> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <!--启动级别-1,在Tomcat启动时就初始化Spring容器--> <load-on-startup>1</load-on-startup> </servlet> <!--/ 匹配所有的请求;(不包括.jsp)--> <!--/* 匹配所有的请求;(包括.jsp)--> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
5 编写springmvc-servlet.xml配置文件
编写springmvc-servlet.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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 --> <context:component-scan base-package="com.example.ajax.controller"/> <!-- 让Spring MVC不处理静态资源 --> <mvc:default-servlet-handler /> <!-- 支持mvc注解驱动 在spring中一般采用@RequestMapping注解来完成映射关系 要想使@RequestMapping注解生效 必须向上下文中注册DefaultAnnotationHandlerMapping 和一个AnnotationMethodHandlerAdapter实例 这两个实例分别在类级别和方法级别处理。 而annotation-driven配置帮助我们自动完成上述两个实例的注入。 --> <mvc:annotation-driven /> <!--视图解析器--> <!--通过视图解析器解析处理器返回的逻辑视图名并传递给DispatcherServlet--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--后缀--> <property name="suffix" value=".jsp"/> </bean> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="failOnEmptyBeans" value="false"/> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> </beans>
6 编写前端JSP视图文件
接下来我们要编写前端的请求文件,格式如下:
<%-- Created by IntelliJ IDEA. User: 13304 Date: 2021/9/12 Time: 15:02 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>ajax</title> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <script> function username(){ $.post({ url:"${pageContext.request.contextPath}/user/argcheck", data:{'name':$("#name").val()}, success:function (data) { if (data.toString()=='OK'){ $("#userInfo").css("color","green"); }else { $("#userInfo").css("color","red"); } $("#userInfo").html(data); } }); } function password(){ $.post({ url:"${pageContext.request.contextPath}/user/argcheck", data:{'pwd':$("#pwd").val()}, success:function (data) { if (data.toString()=='OK'){ $("#pwdInfo").css("color","green"); }else { $("#pwdInfo").css("color","red"); } $("#pwdInfo").html(data); } }); } </script> </head> <body> <p> 用户名:<input type="text" id="name" onblur="username()"/> <span id="userInfo"></span> </p> <p> 密码:<input type="text" id="pwd" onblur="password()"/> <span id="pwdInfo"></span> </p> </body> </html>
6 配置Tomcat服务器并测试
最后我们配置并启动Tomcat服务器来测试:
在浏览器中输入:http://localhost:8080/ajax/login.jsp
,返回结果为:
接下来当我们在框中输入内容可以看到ajax请求而页面并没有刷新,观察请求的url我们发现ajax请求了我们的控制器:
然后将结果返回给dom
总结一下
AJAX还是比较经典的,我理解它的常用案例就是通过局部刷新进行逻辑处理,整体页面的资源无需再被处理,也就是我们不需要提交表单给controller,然后controller再将结果打回去进行一个页面刷新操作,在带宽资源紧张的情况下这么使用还是挺方便的。所以AJAX只是一种模式,Struts框架和SpringMVC框架都可以使用,只不过因为SpringMVC可以使用@RestController注解轻易的实现JSON传输,Struts却不易实现,而对于AJAX而言,JSON 可通过 JavaScript 进行解析,更加适合于AJAX 进行传输,所以对于自动集成AJAX这一点,SpringMVC又更胜一筹。