Strut2判断是否是AJAX调用
1. AJAX与传统Form表单
实际上,两者一般都是通过HTTP的POST请求。区别是浏览器提交Form表单后,期望服务器返回一个完整的HTML页面。而AJAX调用是由XMLHttpRequest对象(不同浏览器可能不一样)发出,浏览器期望服务器返回HTML片段即可,具体是JSON、XML等都没有要求。返回到浏览器后如何使用,也是由JS脚本自己决定的。
2. 请求是不是AJAX
那么对于服务器端,如何判断一个HTTP请求是不是AJAX调用?这需要看HTTP的Header。
我们可以通过Header中的x-request-with来判断。尽管不同浏览器发送AJAX请求的对象不同,但是如果使用jQuery发送AJAX请求的话,jQuery内部实现ajax的时候,已经加入了标识。jQuery源码中是这样的:xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");
所以,如果项目的前台页面都是通过jQuery发送AJAX请求的话,这样判断是安全的。
下面是HTTP请求携带的Header信息。
普通Form表单提交
1
2
3
4
5
6
7
8
9
|
===MimeHeaders === accept = */* referer =http://localhost:8080/user2/toQueryPage.action accept-language = zh-CN user-agent = Mozilla/4.0 (compatible; MSIE8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C;.NET4.0E) accept-encoding = gzip, deflate host = localhost:8080 connection = Keep-Alive cache-control = no-cache |
AJAX调用(IE)
1
2
3
4
5
6
7
8
9
10
11
12
|
===MimeHeaders === x-requested-with = XMLHttpRequest accept-language = zh-cn referer =http://localhost:8080/user2/toQueryPage.action accept = application/json, text/javascript,*/*; q=0.01 content-type =application/x-www-form-urlencoded accept-encoding = gzip, deflate user-agent = Mozilla/4.0 (compatible; MSIE8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C;.NET4.0E) host = localhost:8080 content-length = 57 connection = Keep-Alive cache-control = no-cache |
3. 在Action中获得HTTP请求头
在Action类中,通过ServletRequestAware接口获得HttpServletRequest对象,再通过getHeader方法得到我们想要的头信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public abstract class BaseAction <ParamVo extends BaseParamVo, ResultVo extends BaseResultVo> extends ActionSupport implements ServletRequestAware { private static final String AJAX_RESULT_NAME = "ajaxResult" ; private static final String XHR_OBJECT_NAME = "XMLHttpRequest" ; private static final String HEADER_REQUEST_WITH = "x-requested-with" ; /** * Request对象,用来判断请求是否是AJAX调用 */ private HttpServletRequest request; private ParamVo paramVo; private ResultVo resultVo; @Override public String execute() { String resultPage = SUCCESS; try { resultVo = doExecute(paramVo); } catch (BaseException e) { resultPage = ERROR; } if (XHR_OBJECT_NAME.equals(request.getHeader(HEADER_REQUEST_WITH))) { resultPage = AJAX_RESULT_NAME; } return resultPage; } } |
Struts2性能调优拦截器
当我们在工作中需要实现某些小需求时,不妨先进行下简单的调研,看看正在使用的开源框架是否已经具备了我们需要的功能,这样就不用重复发明轮子了。
下面以性能测试为例,看看如何调查Struts2框架是否具备这种功能。
1. struts-default.xml
因为Struts2的许多核心功能都是基于内部拦截器来实现的,所以我们首先要看看它是否有性能调优相关的拦截器。这就需要查看strut2-core-2.3.1.2.jar中的默认配置文件struts-default.xml了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
< span style = "white-space:pre" > </ span >< interceptor name = "alias" class = "com.opensymphony.xwork2.interceptor.AliasInterceptor" /> < interceptor name = "autowiring" class = "com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor" /> < interceptor name = "chain" class = "com.opensymphony.xwork2.interceptor.ChainingInterceptor" /> < interceptor name = "conversionError" class = "org.apache.struts2.interceptor.StrutsConversionErrorInterceptor" /> < interceptor name = "cookie" class = "org.apache.struts2.interceptor.CookieInterceptor" /> < interceptor name = "clearSession" class = "org.apache.struts2.interceptor.ClearSessionInterceptor" /> < interceptor name = "createSession" class = "org.apache.struts2.interceptor.CreateSessionInterceptor" /> < interceptor name = "debugging" class = "org.apache.struts2.interceptor.debugging.DebuggingInterceptor" /> < interceptor name = "execAndWait" class = "org.apache.struts2.interceptor.ExecuteAndWaitInterceptor" /> < interceptornameinterceptorname = "exception" class = "com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor" /> < interceptor name = "fileUpload" class = "org.apache.struts2.interceptor.FileUploadInterceptor" /> < interceptor name = "i18n" class = "com.opensymphony.xwork2.interceptor.I18nInterceptor" /> < interceptor name = "logger" class = "com.opensymphony.xwork2.interceptor.LoggingInterceptor" /> < interceptor name = "modelDriven" class = "com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor" /> < interceptor name = "scopedModelDriven" class = "com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor" /> < interceptor name = "params" class = "com.opensymphony.xwork2.interceptor.ParametersInterceptor" /> < interceptor name = "actionMappingParams" class = "org.apache.struts2.interceptor.ActionMappingParametersInteceptor" /> < interceptor name = "prepare" class = "com.opensymphony.xwork2.interceptor.PrepareInterceptor" /> < interceptor name = "staticParams" class = "com.opensymphony.xwork2.interceptor.StaticParametersInterceptor" /> < interceptor name = "scope" class = "org.apache.struts2.interceptor.ScopeInterceptor" /> < interceptor name = "servletConfig" class = "org.apache.struts2.interceptor.ServletConfigInterceptor" /> < interceptor name = "timer" class = "com.opensymphony.xwork2.interceptor.TimerInterceptor" /> < interceptor name = "token" class = "org.apache.struts2.interceptor.TokenInterceptor" /> < interceptor name = "tokenSession" class = "org.apache.struts2.interceptor.TokenSessionStoreInterceptor" /> < interceptor name = "validation" class = "org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor" /> < interceptor name = "workflow" class = "com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor" /> < interceptor name = "store" class = "org.apache.struts2.interceptor.MessageStoreInterceptor" /> < interceptor name = "checkbox" class = "org.apache.struts2.interceptor.CheckboxInterceptor" /> < interceptor name = "profiling" class = "org.apache.struts2.interceptor.ProfilingActivationInterceptor" /> < interceptor name = "roles" class = "org.apache.struts2.interceptor.RolesInterceptor" /> < interceptor name = "annotationWorkflow" class = "com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" /> < interceptor name = "multiselect" class = "org.apache.struts2.interceptor.MultiselectInterceptor" /> |
Struts2像个百宝箱一样内置了很多拦截器,可以看到profiling很可能就是符合我们需求的拦截器,那现在就打开源码一探究竟。
2. ProfilingActivationInterceptor
org.apache.struts2.interceptor.ProfilingActivationInterceptor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public class ProfilingActivationInterceptor extendsAbstractInterceptor { private String profilingKey = "profiling" ; private boolean devMode; @Inject (StrutsConstants.STRUTS_DEVMODE) public void setDevMode(String mode) { this .devMode = "true" .equals(mode); } @Override public String intercept(ActionInvocationinvocation) throws Exception { if (devMode) { Object val =invocation.getInvocationContext().getParameters().get(profilingKey); if (val != null ) { String sval = (val instanceof String ?(String)val : ((String[])val)[ 0 ]); boolean enable = "yes" .equalsIgnoreCase(sval)|| "true" .equalsIgnoreCase(sval); UtilTimerStack.setActive(enable); invocation.getInvocationContext().getParameters().remove(profilingKey); } } return invocation.invoke(); } } |
从源码中可以看到,只要浏览器发过来的HTTP请求参数中包含profiling=true或者yes,性能拦截器就会开启Timer工具类,打印出Action的执行消耗时间。
3. struts.xml
因为profiling拦截器没有包含到默认的defaultStack中,所以我们要先将它追加到我们自定义的拦截器栈中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
< package name = "ajax-default" extends = "velocity-default" > < result-types > < result-type name = "json" class = "org.apache.struts2.json.JSONResult" /> </ result-types > < interceptors > < interceptor-stacknameinterceptor-stackname = "ajaxInterceptorStack" > < interceptor-refnameinterceptor-refname = "defaultStack" /> < interceptor-ref name = "profiling" /> </ interceptor-stack > </ interceptors > < default-interceptor-refnamedefault-interceptor-refname = "ajaxInterceptorStack" /> < global-results > < result name = "comAjaxResult" type = "json" > < param name = "excludeNullProperties" >true</ param > < param name = "root" >result</ param > < param name = "ignoreHierarchy" >false</ param > </ result > </ global-results > </ package > |
4. userview.js
现在就可以修改AJAX调用参数,追加上profiling参数就可以开始性能调优了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function searchAllUser(){ jQuery.ajax({ type: "post" , url: "searchAllUser.action" , processData: true , dataType: 'json' , data:jQuery( "#userQueryForm" ).serialize() + "&profiling=yes" , success: function (data) { if (data.status == 1) { alert( "创建成功" ); generateTableFromJson( "result" , data.resultRows); } else { alert( "创建失败" ); } } }); } |
5. 最终效果
打印结果就是下面这样。除了总执行时间外,Action方法的执行时间和Result的渲染时间都会分别列出。