本文不属原创,只是日常工作的一点总结。下面的工程示例只是展示了最基本的配置,能够测试Hello World通过。
1,web.xml配置;
2,常用三方包;
3,Struts2、Spring Bean配置;
4,log4j、国际化资源文件;
5,三层结构设计;
6,异常体系结构;
7,工程结构图。
----------------------------------------------------------------------------------------------------------------------------
1,web.xml配置
一个Listener两个Filter,加载Spring容器上下文,初始化struts2中央转发器。
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>ICCardManager</display-name> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>encodingFilter</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> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:applicationContext-*.xml </param-value> </context-param> <welcome-file-list> <welcome-file>jsp/index.html</welcome-file> <welcome-file>jsp/index.htm</welcome-file> <welcome-file>jsp/login.jsp</welcome-file> <welcome-file>jsp/default.html</welcome-file> <welcome-file>jsp/default.htm</welcome-file> <welcome-file>jsp/default.jsp</welcome-file> </welcome-file-list> </web-app>
2、常用三方包
struts2、spring、Oracle、数据库连接池驱动包、dom4j、Jackson、POI以及iText等。
3、Struts2、Spring Bean配置
struts.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- if devMode=true,modify this file don't need to restart tomcat --> <constant name="struts.devMode" value="false" /> <constant name="struts.objectFactory" value="spring" /> <constant name="struts.action.extension" value="action,do,aspx" /> <constant name="struts.multipart.maxSize" value="1009715200" /> <!-- include multiple config--> <include file="struts-default.xml" /> <include file="struts-icc.xml" /> </struts>
自定义struts-icc.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.custom.i18n.resources" value="icc"></constant> <package name="ICCardManager" namespace="/" extends="struts-default"> <global-results> <result name="failed">jsp/error.jsp</result> <result name="main">jsp/HomeManager.jsp</result> </global-results> <action name="login" class="LoginAction"> <result name="success">/jsp/HomeManager.jsp</result> <result name="login">/jsp/login.jsp</result> </action> </package> </struts>
Spring上下文监听器加载指定配置文件*:applicationContext-*.xml,在src目录下新建文件applicationContext-icc.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="LoginAction" class="cn.hunnu.icc.action.LoginAction" scope="prototype"> </bean> <!-- Service Beans --> <bean id="UserService" class="cn.hunnu.icc.service.impl.UserService"> <property name="userDAO"> <ref bean="UserDAO"/> </property> </bean> <bean id="CardService" class="cn.hunnu.icc.service.impl.CardService"> <property name="cardDAO"> <ref bean="CardDAO"/> </property> </bean> <!-- DAO Beans --> <bean id="UserDAO" class="cn.hunnu.icc.dao.impl.UserDAO"> </bean> <bean id="CardDAO" class="cn.hunnu.icc.dao.impl.CardDAO"> </bean> </beans>
4,log4j、国际化资源文件
4.1 日志管理log4j.properties
log4j.rootCategory=INFO, stdout , R log4j.logger.cn.hunnu.icc=INFO,ICC log4j.appender.ICC=org.apache.log4j.DailyRollingFileAppender log4j.appender.ICC.File=${catalina.home}/ICC/logs/ICC.log log4j.appender.ICC.layout=org.apache.log4j.PatternLayout log4j.appender.ICC.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n log4j.appender.R=org.apache.log4j.DailyRollingFileAppender log4j.appender.R.File=${catalina.home}/ICC/logs/qc.log log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n log4j.logger.com.neusoft=DEBUG log4j.logger.com.opensymphony.oscache=ERROR log4j.logger.net.sf.navigator=ERROR log4j.logger.org.apache.commons=ERROR log4j.logger.org.apache.struts=WARN log4j.logger.org.displaytag=ERROR log4j.logger.org.springframework=INFO log4j.logger.com.ibatis.db=WARN log4j.logger.org.apache.velocity=FATAL log4j.logger.com.canoo.webtest=WARN
4.2 国际化资源文件在struts.xml中指定了其命名,我们采用struts2默认的国际化支持
<constant name="struts.custom.i18n.resources" value="icc"></constant>
资源文件列表:
icc_en_US.properties
icc_zh_CN.properties
icc.properties
美国英文资源icc_en_US.properties示例:
homemanager.toolbar.action=Action homemanager.toolbar.action.newnote=New Note homemanager.toolbar.action.duplicatenote=Duplicate Note homemanager.toolbar.action.deletenote=Delete Note(s) homemanager.toolbar.action.newfilter=New Filter homemanager.toolbar.action.shareoutside=Share Outside My Firm homemanager.toolbar.export=Export homemanager.toolbar.print=Print homemanager.toolbar.columnview=Column View homemanager.toolbar.setting=Default Note Settings homemanager.tab.filter=Filter Settings homemanager.tab.detail=Note Settings homemanager.tab.content=Note Content panel.filter.ApplyFilter=Apply Filter panel.filter.Effective=Effective panel.filter.to=to panel.filter.Modified=Modified panel.filter.SearchDefault=search keywords or phrases
5、三层结构设计
5.1 DAO接口以及实现类
package cn.hunnu.icc.dao; public interface ICardDAO { //接口列表... }
package cn.hunnu.icc.dao; public interface IUserDAO { //接口列表... }
package cn.hunnu.icc.dao.impl; import cn.hunnu.icc.dao.ICardDAO; public class CardDAO implements ICardDAO { //接口实现列表... }
package cn.hunnu.icc.dao.impl; import cn.hunnu.icc.dao.IUserDAO; public class UserDAO implements IUserDAO { //接口实现列表... }
5.2 Service接口以及实现类
package cn.hunnu.icc.service; public interface ICardService { //接口列表... }
package cn.hunnu.icc.service; public interface IUserService { //接口列表... }
package cn.hunnu.icc.service.impl; import cn.hunnu.icc.dao.ICardDAO; import cn.hunnu.icc.service.ICardService; public class CardService implements ICardService { private ICardDAO cardDAO; public ICardDAO getCardDAO() { return cardDAO; } public void setCardDAO(ICardDAO cardDAO) { this.cardDAO = cardDAO; } }
package cn.hunnu.icc.service.impl; import cn.hunnu.icc.dao.IUserDAO; import cn.hunnu.icc.service.IUserService; public class UserService implements IUserService { private IUserDAO userDAO; public IUserDAO getUserDAO() { return userDAO; } public void setUserDAO(IUserDAO userDAO) { this.userDAO = userDAO; } }
关于究竟是在Service层调用多个DAO层,还是Service层调用多个Service层,在网上各有争论。
service - > dao:
service - > service
5.3 Action层调用Service接口
package cn.hunnu.icc.action; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts2.ServletActionContext; import cn.hunnu.icc.service.IUserService; /** * 用户登录 * @author Administrator * */ public class LoginAction extends BaseAction { private static final long serialVersionUID = -2234417925314808583L; public static final Log log = LogFactory.getLog(LoginAction.class); private IUserService userService; private String account; private String pwd; public String execute() { log.info("execute HelloAction..."); HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("userName", account); boolean success = false; if("xiaobo".equals(account)) { success = true; } if(success) { return SUCCESS; } else { request.setAttribute("msg", "对不起,您的输入有误,请重新输入!"); return LOGIN; } } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public IUserService getUserService() { return userService; } public void setUserService(IUserService userService) { this.userService = userService; } }
注意BaseAction,这是一个自定义的通用Action,提供了一些常用方法,下面展示其源码:
package cn.hunnu.icc.action; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.apache.struts2.ServletActionContext; import cn.hunnu.icc.util.Constants; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class BaseAction extends ActionSupport { protected static final Logger LOGGER = Logger.getLogger(BaseAction.class); protected static final String DEFAULT_CHARACTER = Constants.DEFAULT_CHARACTER; protected static final String ContentType_HTML = "text/html"; protected static final String ContentType_XML = "text/xml"; protected static final String ContentType_JSON = "application/json"; /** * get project root directory. * * @return */ protected String getWebRealPath() { return ServletActionContext.getServletContext().getRealPath("/"); } /** * get request of parameter value. * * @param parameter * @return string parameter value,if this value is null then return "". */ protected String getParameter(String parameter) { Object value = getParameters().get(parameter); if (value != null) { return ((String[]) value)[0]; } return ""; } protected HttpServletRequest getRequest() { return ServletActionContext.getRequest(); } protected HttpServletResponse getResponse() { return ServletActionContext.getResponse(); } protected ServletContext getServletContext() { return ServletActionContext.getServletContext(); } /** * get request of parameter values. * * @param parameter * @return string parameter values. */ protected String[] getParameterValues(String parameter) { String[] values = this.getRequest().getParameterValues(parameter); if (values != null) { return values; } return new String[] {}; } /** * get request of int parameter value. * * @param parameter * @return int */ protected int getIntParameter(String parameter) { Object value = getParameters().get(parameter); if (value != null) { try { return Integer.parseInt(((String[]) value)[0]); } catch (Exception e) { return 0; } } return 0; } @SuppressWarnings("unchecked") protected Map getParameters() { ActionContext actionContext = ActionContext.getContext(); Map parameters = (Map) actionContext.getParameters(); return parameters; } /** * get web deployment Path. * * @return */ public String getContextPath() { return ServletActionContext.getServletContext().getRealPath("/"); } /** * The information directly back to the browser,Default character is utf-8. * * @param content * @throws IOException */ protected void sendClient(String content) { this.sendClient(content, DEFAULT_CHARACTER, ContentType_HTML); } /** * The information directly back to the browser. * * @param content * @param character * @throws IOException */ protected void sendClient(String content, String character) throws IOException { sendClient(content, character, ContentType_XML); } /** * The information directly back to the browser. * * @param content * @param character * @throws IOException */ protected void sendClient(String content, String character, String contentType) { HttpServletResponse response = ServletActionContext.getResponse(); response.setContentType(String.format("%s;charset=%s", contentType, character)); PrintWriter out; try { out = response.getWriter(); out.print(content); out.flush(); out.close(); } catch (IOException e) {} } /*** * Send xml data to client, utf-8 encoding * @param content * @throws IOException */ protected void sendClientXML(String content) throws IOException { this.sendClient(content, DEFAULT_CHARACTER, ContentType_XML); } /*** * Send JSON data to client, utf-8 encoding * @param content * @throws IOException */ protected void sendClientJSON(String content) throws IOException { this.sendClient(content, DEFAULT_CHARACTER, ContentType_JSON); } /** * * @param queryString * @return */ @SuppressWarnings("unchecked") protected Map getParameterMap(String queryString) { if (queryString == null || "".equals(queryString)) return null; Map parameterMap = new HashMap(); String[] parArray = queryString.split("&"); for (int i = 0; i < parArray.length; i++) { if (parArray[i].split("=").length == 2) { parameterMap.put(parArray[i].split("=")[0], parArray[i] .split("=")[1]); } } return parameterMap; } protected String getProperty(String key) { String ret = getRequest().getParameter(key); if (ret==null || ret.isEmpty()) { return getRequest().getAttribute(key).toString(); } else { return ret; } } }
6,异常体系结构
设计异常的基本原则是:
(1) DAO层所有的接口都不声明checked异常,但建议都声明unchecked异常,DAO实现类统一使用RuntimeException,可以参考Spring Jdbctemplate设计原则,举例:
public void update(Task task);
(2) Service层接口统一抛出自定义的Service异常,并且在Service实现类中所有的DAO操作都要try..catch..,举例:
public interface ITaskService { public void updateTask(Task task) throws ServiceException; } public class TaskService implements ITaskService { ITaskDAO taskdao; IUserDAO userdao; public void updateTask(Task task) throws ServiceException { try { taskdao.update(task); } catch(Exception e) { throw new ServiceException(“code+message”); } //other logic operations try { user = userdao.getUser(env); } catch(Exception e) { throw new ServiceException(“code+message”); } } }
(3) 在Action捕获Service异常,可以根据ServiceException的code和message进行异常归类和异常消息国际化处理。
7,工程结构图