struts2中Convention plug-in中的那些事情
哈哈,一颗好奇心、一颗永不满足的心让我终于解决了这个问题。
解决问题来源:官方的教程(英文的,虽然英语不咋地,还是硬着头皮给看完了。(*^__^*) 嘻嘻……),获取办法google搜索struts2然后找到带有apache的网址,也就是在www.apache.org中的一个分支了。
下面就说一下里面的具体问题吧:
当我们需要用到注解的时候,需要引入一个jar包,名字为:struts2-convention-plugin-2.3.1.2.jar,先提示一下,千万别小看这个包哦,这个包可是很“智能”的,如果你很想自己手工完成某些操作,而你引入了这个包,它很可能破坏你的打算哦。下面就看一下这个包的作用了:
默认情况下Convention plugin监听包名含有:stru、struts2、action或者actions这些包中的action类,也就是说如果你建立的包名最后包含上述这几个类型,那么action就会自动的将它作为你的action处理类所在的包。
那么我们自己编写的action呢就应该实现action接口或者以action结尾,那么Convention plugin就会自动识别这些类。
并且啊,这个Convention plugin还会用类的名字去匹配URL,也就是说:如果你发出的一个请求是请求hello.action,如果你有一个类名为HelloAction(可以不实现Action接口)或者类名为Hello(实现Action接口),那么你就可以不进行任何的配置(指的是struts.xml中配置action和URL的映射,并且也没有采用注解方式配置)就可以形成一个Action和URL的映射。神奇吧,这个就是Convention plugin的智能,别急,不要急着去实践,下面还有呢。。。
此刻Convention plugin已经知道了如何的将请求和Action对应起来,那么它是否可以智能的将结果和结果页面对应呢。当你看到这个问题的时候估计和我的想法一样:这怎么可能呢,返回的结果有那么多的情况:input、error、success等等,它怎么可以自动的寻找映射呢。(*^__^*) 嘻嘻……那你也太小看Convention plugin这个包了,它的确可以形成映射,但是前提就是你的结果页面必须的符合一定的命名规则,并且存储的路径也不能随便存储哦。它的具体规则为:1、通常情况下Convention plugin包会默认为处理的结果页面位于WEB-INF/content这个路径下(此路径可以在struts.properties中进行修改,要修改的属性为:上struts.convention.result.path );2、结果的命名也就是在你请求的页面后面加上结果信息了,例如:刚刚请求的是Hello,那么你可以在前面要求的路径下建立一个hello.jsp(返回success时会执行)、hello-success.jsp(返回success时会执行)、hello-input.jsp(返回input的时候会执行),剩下的就依葫芦画瓢了,也就是”你的请求”+”-”+”结果类型”.jsp,当然还可以有其它的类型,例如:freeMaker等。
看到这儿,基本上那个问题都已经解决了。
那么下面,我们就看一下官方提供的一个示例源代码了:
在看源代码前,先看一下整体的命名规则及其所需要引入的库:
先看一下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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>Struts2Example1</display-name> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
再看一下Index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@taglib uri="/struts-tags" prefix="s" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Hello World</title> </head> <body> <s:form action="welcome-user" > <s:textfield name="userName" label="User Name" /> <s:submit /> </s:form> </body> </html>
下面的是URL对应的action:
package com.vaannila.action; import com.opensymphony.xwork2.ActionSupport; public class WelcomeUser extends ActionSupport{ private String userName; private String message; public String execute() { message = "Welcome " + userName; return SUCCESS; } public void setUserName(String userName) { this.userName = userName; } public void setMessage(String message) { this.message = message; } public String getUserName() { return userName; } public String getMessage() { return message; } }
<!--[endif]-->下面的是用来显示结果的页面:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Welcome User</title> </head> <body> <h1>${message}</h1> </body> </html>
至此已经完成了。。。。
还有一个顺便提醒一下:不知大家有没有留意什么时候保存.java文件的时候服务器会自动重启。。。我的观察结果:如果你的类不是重要的类(处理用户请求的类为重要类)或者文件不是重要的配置文件,那么服务器就不会重启,因为此刻牵扯到如果你的action放在一个包名不为convention plugin所自动化解析的包中的时候,并且你采用的是注解方式配置(因为我只做了这个测试,所以此处只说这种情况,其它的大家可以自己测试),那么你修改了action类,服务器不会重启。
觉得还是分析一下源代码比较好。。。。要不然总感觉到有些玄乎。。。
此刻我没有采用单步调试,只是简单的看了一下convention plug-in包中的部分代码,如果谁进行单步调试并分析了结果还希望可以分享一下哦。。。
下面总结一下看的结果:
org.apache.struts2.convention. DefaultResultMapBuilder这个类中有一个函数就实现了结果页面的自动映射,其实看这个类的名字也很容易发现的:默认生成结果映射
/** * Makes all the results for the given path. * * @param actionClass The action class the results are being built for. * @param path The path to build the result for. * @param resultPrefix The is the result prefix which is the result location plus the action name. * This is used to determine if the path contains a result code or not. * @param results The Map to place the result(s) * @param packageConfig The package config the results belong to. * @param resultsByExtension The map of extensions to result type configuration instances. */ protected void makeResults(Class<?> actionClass, String path, String resultPrefix, Map<String, ResultConfig> results, PackageConfig packageConfig, Map<String, ResultTypeConfig> resultsByExtension) { if (path.startsWith(resultPrefix)) { int indexOfDot = path.indexOf('.', resultPrefix.length()); // This case is when the path doesn't contain a result code if (indexOfDot == resultPrefix.length() || !flatResultLayout) { if (LOG.isTraceEnabled()) { LOG.trace("The result file [#0] has no result code and therefore" + " will be associated with success, input and error by default. This might" + " be overridden by another result file or an annotation.", path); } //下面就是分析是否包含有返回结果类型的页面了 if (!results.containsKey(Action.SUCCESS)) { ResultConfig success = createResultConfig(actionClass, new ResultInfo(Action.SUCCESS, path, packageConfig, resultsByExtension), packageConfig, null); results.put(Action.SUCCESS, success); } if (!results.containsKey(Action.INPUT)) { ResultConfig input = createResultConfig(actionClass, new ResultInfo(Action.INPUT, path, packageConfig, resultsByExtension), packageConfig, null); results.put(Action.INPUT, input); } if (!results.containsKey(Action.ERROR)) { ResultConfig error = createResultConfig(actionClass, new ResultInfo(Action.ERROR, path, packageConfig, resultsByExtension), packageConfig, null); results.put(Action.ERROR, error); } // This case is when the path contains a result code } else if (indexOfDot > resultPrefix.length()) { if (LOG.isTraceEnabled()) { LOG.trace("The result file [#0] has a result code and therefore" + " will be associated with only that result code.", path); } String resultCode = path.substring(resultPrefix.length() + 1, indexOfDot); ResultConfig result = createResultConfig(actionClass, new ResultInfo(resultCode, path, packageConfig, resultsByExtension), packageConfig, null); results.put(resultCode, result); } } }
..................................