【前言】
所谓快速开发,实质上为了节省项目的开支成本,减少程序员的开发时 间,固然就形成了种种二次封装的框架,也就是造轮子,然后我们的程序就按照这个轮子去画瓢,这里我就把公司这几次开发系统的框架源码贴出来,做一下讲解以 及使用示范,并有附件提供参考,希望能给各位在基于后台管理系统提供帮助。
【目录】
11.struts2的配置
22.struts2的基类构成
33.具体实例应用
【内容】
一、struts2的配置
1 .struts2的过滤器拦截配置 :相信大家对struts2的配置一定不陌生,这里我就简单带过struts2过滤器在web.xml的配置:
1<filter>
2 <filter-name>struts2</filter-name>
3 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
4</filter>
5<filter-mapping>
6 <filter-name>struts2</filter-name>
7 <url-pattern>*.action</url-pattern>
8</filter-mapping>
2 .struts2全局配置:这里我要做一点介绍了,按常理来讲,我们都是基于约定大于配置的原则,不做重复劳动的思想:
A.基于Annotation的配置(struts2有一种叫convention的插件,可以实现零配置,具体可以百度Struts2 convention,我就不做讲解,注:这里的零配置是针对struts2在xml中配置)。
B.采用通配符进行配置,也就是我推荐的,也是我们项目中用到的一种方法,先上代码struts.xml:
1<?xml version="1.0" encoding="UTF-8" ?>
2<!DOCTYPE struts PUBLIC
3 "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
4 "http://struts.apache.org/dtds/struts-2.0.dtd">
5<struts>
6 <package name="default" namespace="/" extends="struts-default">
7
8
9 <global-results>
10 <result name="error">/page/error.jsp</result>(1)
11 <result name="input"></result>(1)
12 <result name="login" type="redirect">/login.jsp</result>(1)
13 <result>${target}</result>(2)
14 <result type="redirectAction" name="redirectAction">${target}</result>(2)
15 <result type="redirect" name="redirect">${target}</result>(2)
16 </global-results>
17 </package>
18 <package name="app" namespace="/" extends="default">(4)
19 <action name="*/*/*" class="st.{1}.web.{2}Action" method="{3}">(3)
20 </action>
21 </package>
22</struts>
解析:上面我对配置文件中加入(1)、(2)、(3)、(4),表示对此处的标注,下面我就对这些部分进行解析。
(1):如果有用过struts2的童鞋,可以清晰的看出它们名称都很有含义,对没错 struts2的Action类已经封装这4个静态常量,当我们在Action的方法中完成操作后,要对视图进行 跳转或者响应时,要返回一个字符串,比如:return SUCCESS;那么这个SUCCESS实际上就是对应我们在struts2配置的xxxx.jsp,简化一下也就是xxx.jsp,默认的result name就是"success" 默认的type是dispatch 也就是JSP视图,那么这样我们就可以对这些预定好的变量配置他们响应的跳转视图页面。
(2):很奇怪为什么有个{target},{target}只是作为struts2的配置的占位参数,而基类BaseAction中我们存放了一个 target变量,当解析struts.xml时,可以从我们实现类中读 取出来,然后进行替换,这样做的好处是什么,很显然,我们要返回的jsp页面有成千上万种,我不可能每次都去配置 a.jsp b.jsp….,这样可以说是实现了统一设置路径,也就是通配,而且还有不同类型的返回,type="redirect"/ type="redirectAction",看上去好像是一样的,区别在于redirectAction可以指定重定向到某个action上 (struts2中配置的action),target就是Action的name,值得注意的是我这里只是罗列了重定向以及Jsp视图的类型,要知道还 有其他N多种类型的视图,怎么办?我先把关于模板类型的返回的告知出来以freemaker为例,其他见基类BaseAction:struts.xml 中加入freemaker
name="freemaker",{target},其实道理是一样 只是type不一样而已,target="xxxx.ftl";(3):这个是很重要的,我们所有的action都用了通用,原则是:{模块名}/{用例名}/{方法}.action 比如:st.sys.web.UserAction.java - sttbas_server/src/java这样一个类,我们访问它的查询方法:sys/User/view.action,很简单。
(4):我们这个名为app的package继承了 default包,也就意味可以重用default的配置。好像struts.xml少了点什么东西,对我们没有配置它的属性,这里我们将struts2的属性配置进行区分,也是官方推荐的设置将 struts.xml,与struts.properties分开,并放入classpath目录下,struts.properties内容如下:
1#允许使用下划线#
2struts.enable.SlashesInActionNames=true //必须要的,因为我们配置Action时用了下划线
3#spring做为对象管理工厂#
4struts.objectFactory=spring
5#开发模式#
6struts.devMode=false//开发阶段可以开启这个模式
7#静态缓存#
8struts.serve.static.browserCache=false //开发阶段可以开启
9#配置是否重新加载#
10struts.configuration.xml.reload=true //建议开启,方便调试,发布版本的时候可以关闭
当然还有很多属性的配置,详细可以参考struts2源码包中struts.properties
二、struts2的基类
1package com.st.web.action;
2import java.io.IOException;
3import java.util.Map;
4import javax.servlet.http.HttpServletRequest;
5import javax.servlet.http.HttpServletResponse;
6import javax.servlet.http.HttpSession;
7import org.apache.commons.lang.StringUtils;
8import org.apache.commons.logging.Log;
9import org.apache.commons.logging.LogFactory;
10import org.apache.struts2.ServletActionContext;
11import org.apache.struts2.interceptor.ServletRequestAware;
12import org.apache.struts2.interceptor.ServletResponseAware;
13import org.apache.struts2.interceptor.SessionAware;
14import com.opensymphony.xwork2.ActionContext;
15import com.opensymphony.xwork2.ActionSupport;
16import com.opensymphony.xwork2.ModelDriven;
17import com.opensymphony.xwork2.Preparable;
18import com.st.utils.AjaxMsg;
19import com.st.utils.JsonUtil;
20/**
21* @author fisher
22* @description Action公共基类
23* @param 所有对象类型
24*/
25public abstract class BaseAction extends ActionSupport implements
26 ModelDriven, Preparable, SessionAware, ServletRequestAware,
27 ServletResponseAware {
28
29 private static final long serialVersionUID = 1L;
30 protected Log log = LogFactory.getLog(getClass());
31 // -- header 常量定义 --//
32 private static final String HEADER_ENCODING = "encoding";
33 private static final String HEADER_NOCACHE = "no-cache";
34 private static final String DEFAULT_ENCODING = "UTF-8";
35 private static final boolean DEFAULT_NOCACHE = true;
36 // -- Content Type 定义 --//
37 public static final String TEXT_TYPE = "text/plain";
38 public static final String JSON_TYPE = "application/json";
39 public static final String XML_TYPE = "text/xml";
40 public static final String HTML_TYPE = "text/html";
41 public static final String JS_TYPE = "text/javascript";
42 public static final String EXCEL_TYPE = "application/vnd.ms-excel";
43 public static final String IMG_TYPE = "image/jpeg";
44 // -------------------- 作用域对象 --------------------//
45 /** sessionMap对象 */
46 private Map sessionMap;
47 /** request对象 */
48 private HttpServletRequest request;
49 /** response对象 */
50 private HttpServletResponse response;
51 /** ajax消息 */
52 public static final AjaxMsg ajaxMsg = new AjaxMsg();
53 // --------------------视图跳转路径------------------//
54 /**
55 * 例如:"index.jsp index.ftl"
56 */
57 private String target;
58 // -- Preparable函数 --//
59 /**
60 * 实现空的prepare()函数,屏蔽了所有Action函数都会执行的公共的二次绑定.
61 */
62 public void prepare() throws Exception {
63 }
64 // -------------绕过jsp/freemaker直接输出文本函数-------------//
65 // -- 绕过jsp/freemaker直接输出文本的函数 --//
66 /**
67 * 直接输出内容的简便函数. eg. render("text/plain", "hello", "encoding:GBK");
68 * render("text/plain",hello", "no-cache:false");
69 * render("text/plain","hello", "encoding:GBK","no-cache:false");
70 *
71 * @param headers
72 * 可变的header数组,目前接受的值为"encoding:"或"no-cache:",默认值分别为UTF-8和true.
73 */
74 public static void render(final String contentType, final String content,
75 final String... headers) {
76 HttpServletResponse response = initResponseHeader(contentType, headers);
77 try {
78 response.getWriter().write(content);
79 response.getWriter().flush();
80 } catch (IOException e) {
81 throw new RuntimeException(e.getMessage(), e);
82 }
83 }
84 public static void renderImg(final String img, final String... headers) {
85 render(IMG_TYPE, img, headers);
86 }
87 /**
88 * 直接输出文本.
89 *
90 * @see #render(String, String, String...)
91 */
92 public static void renderText(final String text, final String... headers) {
93 render(TEXT_TYPE, text, headers);
94 }
95 /**
96 * 直接输出HTML.
97 *
98 * @see #render(String, String, String...)
99 */
100 public static void renderHtml(final String html, final String... headers) {
101 render(HTML_TYPE, html, headers);
102 }
103 /**
104 * 直接输出XML.
105 *
106 * @see #render(String, String, String...)
107 */
108 public static void renderXml(final String xml, final String... headers) {
109 render(XML_TYPE, xml, headers);
110 }
111 /**
112 * 直接输出JSON.
113 *
114 * @param jsonString
115 * json字符串.
116 * @see #render(String, String, String...)
117 */
118 public static void renderJson(final String jsonString,
119 final String... headers) {
120 render(JSON_TYPE, jsonString, headers);
121 }
122 /**
123 * 直接输出JSON,使用fastJson转换Java对象.
124 *
125 * @param data
126 * 可以是List, POJO[], POJO, 也可以Map名值对.
127 * @see #render(String, String, String...)
128 */
129 public static void renderJson(final Object data, final String... headers) {
130 String jsonString = JsonUtil.serialize(data);
131 renderJson(jsonString, headers);
132 }
133 public static void renderJson(final Object data,
134 final String[] propertyFilter, final boolean isInclude,
135 final String... headers) {
136 String jsonString = JsonUtil.serialize(data, propertyFilter, isInclude);
137 renderJson(jsonString, headers);
138 }
139 /**
140 * 分析并设置contentType与headers.
141 */
142 private static HttpServletResponse initResponseHeader(
143 final String contentType, final String... headers) {
144 // 分析headers参数
145 String encoding = DEFAULT_ENCODING;
146 boolean noCache = DEFAULT_NOCACHE;
147 for (String header : headers) {
148 String headerName = StringUtils.substringBefore(header, ":");
149 String headerValue = StringUtils.substringAfter(header, ":");
150 if (StringUtils.equalsIgnoreCase(headerName, HEADER_ENCODING)) {
151 encoding = headerValue;
152 } else if (StringUtils.equalsIgnoreCase(headerName, HEADER_NOCACHE)) {
153 noCache = Boolean.parseBoolean(headerValue);
154 } else {
155 throw new IllegalArgumentException(headerName
156 + "不是一个合法的header类型");
157 }
158 }
159 HttpServletResponse response = ServletActionContext.getResponse();
160 // 设置headers参数
161 String fullContentType = contentType + ";charset=" + encoding;
162 response.setContentType(fullContentType);
163 if (noCache) {
164 // Http 1.0 header
165 response.setDateHeader("Expires", 1L);
166 response.addHeader("Pragma", "no-cache");
167 // Http 1.1 header
168 response.setHeader("Cache-Control", "no-cache, no-store, max-age=0");
169 }
170 return response;
171 }
172 /**
173 * @description 获取磁盘物理路径
174 * @return String
175 */
176 protected String getRealPath() {
177 return request.getSession().getServletContext().getRealPath("");
178 }
179 /**
180 * @description 得到session对象
181 * @return Session
182 */
183 protected HttpSession getSession() {
184 return this.request.getSession();
185 }
186 /**
187 * @Title: getParams
188 * @Description: 得到页面所有参数
189 * @return Map
190 * @throws
191 */
192 public Map getParameters() {
193 return ActionContext.getContext().getParameters();
194 }
195 /**
196 * @description 设置session对象Attribute属性
197 * @param key
198 * @param value
199 */
200 protected void setSessionAttribute(String key, Object value) {
201 sessionMap.put(key, value);
202 }
203 /**
204 * @description 返回session对象的Attribute属性
205 * @param key
206 * @return Object
207 */
208 protected Object getSessionAttribute(String key) {
209 return sessionMap.get(key);
210 }
211 /**
212 * @description 清除整个session
213 */
214 protected void clearSession() {
215 sessionMap.clear();
216 }
217 /**
218 * @description 清除session中指定的内容
219 * @param key
220 */
221 protected void remove(Object key) {
222 sessionMap.remove(key);
223 }
224 /**
225 * @description 通过request获取页面参数值
226 * @return string
227 */
228 protected String getParameters(String name) {
229 return (null != request.getParameter(name)) ? request
230 .getParameter(name) : null;
231 }
232 /**
233 * @description 获取项目根目录
234 * @return String
235 */
236 protected String getRootPath() {
237 return request.getContextPath();
238 }
239 /**
240 * @description 获取request独享
241 * @return
242 */
243 public HttpServletRequest getRequest() {
244 return request;
245 }
246 /**
247 * @description 获取response对象
248 * @return
249 */
250 public HttpServletResponse getResponse() {
251 return response;
252 }
253 /**
254 * @description 获取sessionMap
255 * @return
256 */
257 public Map getSessionMap() {
258 return sessionMap;
259 }
260 public String getTarget() {
261 return target;
262 }
263 public void setTarget(String target) {
264 this.target = target;
265 }
266 public void setServletResponse(HttpServletResponse response) {
267 this.response = response;
268 }
269 public void setServletRequest(HttpServletRequest request) {
270 this.request = request;
271 }
272 public void setSession(Map sessionMap) {
273 this.sessionMap = sessionMap;
274 }
275}
解析:
接到上面struts2的配置问题来说,可以看到我们在配置result的时候存在占位参数${target},那这个target是哪里来的?可以看 到,BaseAction中已经包含这个变量,并且提供get方法,有童鞋会问为什么这样子对target设置就能替换{target},我只能说这是 OGNL的一种机制。
可以看出baseAciton中定义了很多常量,怎么用?这些常量定义是为了处理response 的响应头,上面我有讲过,我没有在struts.xml中处理流返回类型,json返回类型,对吧?很显然是要在这里做操作了撒,可以看出有很多 render开头的重载方法,这些方法就是帮助我们直接去响应前台,后面例子中我会附带系统中struts2对json处理、对流文件(excel、 img)处理。
实现了很多接口,值得注意的是Preparable和ModelDriven。注意这个BaseAction是泛型,就是因为ModelDriven(模型驱动),有了它我们可以直接由ModelDriven的interceptor拦截前台参数,把参数分装成我们指定的泛型类型的对象,而Preparable则是提供给一个预处理接口,让在执行Action方法之前执行。其他几个几口是帮助我们获取作用域对象,session、request。
附件中会包含基类的引用类,JsonUtil(json工具类)以及AjaxMsg(ajax消息类,针对Json消息)
三、具体示例
示例我写得很简单,大概就是举例一下,如何进行前后台的交互,以及响应不同返回类型。由于帖子长度有限,所以基类的代码和示例代码大家只有自己来开发了,写这篇文章耗费了我3个小时,由于文笔水品有限,有瑕疵的地方,请大家指正批评。