SSH2框架设计---代码整合

         SSH2框架对实体层和表现层进行了很好的封装,并通过Spring注入的方式解决了各层之间的耦合,本文章对该框架部分实现进行代码整合,提高复用率。


       首先我们了解一下SSH2框架的基本流程:



         常见的代码整合部分如下图:




        我们按照开发从前台到后台的顺序进行分析:


        【1.jsp页面整合】

   

        最常见的应用是公共引用和组件部分进行抽取,jsp提供了一种.jspf格式的公共抽取文件,在jsp页面引用该文件时添加:

<%@ includefile="/WEB-INF/jsp/public/commons.jspf"%>


例如分页组件的提取:pageView.jspf

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<div id=PageSelectorBar>
			<div id=PageSelectorMemo>
				页次:${currentPage}/${pageCount}页  
				每页显示:${pageSize}条  
				总记录数:${recordCount}条
			</div>
			<div id=PageSelectorSelectorArea>
			
				<a href="javascript:gotoPage{1}" title="首页" style="cursor: hand;">
					<img src="${pageContext.request.contextPath}/style/blue/images/pageSelector/firstPage.png"/>
				</a>
				<s:iterator begin="%{beginPageIndex}" end="%{endPageIndex}" var="num">
					
					<s:if test="#num==currentPage">
						<!-- 当前页 -->
						<span class="PageSelectorNum PageSelectorSelected">${num}</span>
					</s:if>
					<s:else>
						<!-- 非当前页 -->
						<span class="PageSelectorNum" style="cursor: hand;" onClick="gotoPage(${num});">${num }</span>
					</s:else>
				</s:iterator>
				
				
				<a href="javascript:gotoPage(${pageCount})" title="尾页" style="cursor: hand;">
					<img src="${pageContext.request.contextPath}/style/blue/images/pageSelector/lastPage.png"/>
				</a>
				
				转到:
				<select οnchange="gotoPage(this.value)" id="_pn">
					<s:iterator begin="1" end="%{pageCount}" value="num">
						<option value="${num}">${num}</option>
					</s:iterator>
				</select>
				
				<script type="text/javascript">
					$("#_pn").val("${currentPage}");
				</script>
			</div>
		</div>

		<script type="text/javascript">
			/* function gotoPage(pageNum){
				window.location.href="forum_show.action?id=${id}&pageNum="+pageNum;
			} */
			
			function gotoPage(pageNum){
				$(document).forms[0].apend("<input type='hidden' name='pageNum' value='"+pageNum+"'>");
				document.forms[0].submit();
			}
		</script>

2. action整合


【2.1】统一实现ModelDriven接口(统一把实体类当成页面数据的收集对象,这也是为什么在struts2jsp页面中可以直接写实体属性的原因),但是存在问题:如何获得泛型集合的Class属性?


  解决方法:利用反射

		//定义class:
		private Class<T> clazz=null;  //TODO:无法获得T的class类型
		
		//类设置为abastract类型
		
		//在构造函数利用反射技术获得类型
		public BaseDaoImpl(){
				//使用反射技术获得泛型T的类型
				ParameterizedType pt=(ParameterizedType) this.getClass().getGenericSuperclass();  //获取属性类型
				this.clazz=(Class<T>)pt.getActualTypeArguments()[0];
				System.out.println("clazz---->>"+clazz);

}

      2.2 统一注入service,声明公共变量,如:pageNum,pageSize


3. Utils--工具方法


       【3.1】 监听器:listener

       我们经常会创建一些监听器对对象进行监听,如在页面初始化时,监听初始化对象,加载用户的所有权限地址,用以判断action的请求能否进入系统。

       
public class InitListener implements ServletContextListener {


	public void contextInitialized(ServletContextEvent sce) {
		// 获取容器与相关的Service对象
		ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
		PrivilegeService privilegeService = (PrivilegeService) ac.getBean("privilegeServiceImpl");

		// 准备数据:topPrivilegeList
		List<Privilege> topPrivilegeList = privilegeService.findTopList();
		sce.getServletContext().setAttribute("topPrivilegeList", topPrivilegeList);
		System.out.println("------------> 已准备数据 <------------");
		
		
		//准备数据:allPrivilegeUrls
		Collection<String> allPrivilegeUrls = privilegeService.findAllPrivilegeUrls();
		sce.getServletContext().setAttribute("allPrivilegeUrls", allPrivilegeUrls);
		System.out.println("------------> 已准备数据 <------------");
		
	}

        在web.xml配置文件中进行配置:

       <!-- 配置Spring的用于初始化容器对象的监听器 -->
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

      【 3.2】  拦截器:interceptor

       在权限控制系统中,往往需要验证所有的用户action请求,这时需要我们建立自定义拦截器拦截所有的用户请求,判断用户具有该请求权限方可进入,否则无法完成系统操作:

       
public class CheckPrivilegeInterceptor extends AbstractInterceptor {

	@Override
	public String intercept(ActionInvocation invocation) throws Exception {
		//准备user数据
		User user=(User) ActionContext.getContext().getSession().get("user");
		
		//准备要拦截的url地址
		String namespace=invocation.getProxy().getNamespace();
		String actionName=invocation.getProxy().getActionName();
		String privUrl=namespace+actionName;
		
		//判断是否已经登录,判断是否是去登录,是则放行,否则返回登录
		if(user==null){
			if(privUrl.startsWith("/user_login")){
				return invocation.invoke();
			}else{
				return "loginUI";
			}
		}else{
			//如果登录,则判断权限
			if(user.hasPrivilegeByUrl(privUrl)){
				return invocation.invoke();
			}else{
				return "noPrivilegeError";
			}
		}

	}

}

         在struts.xml配置文件中声明拦截器,且放在默认拦截器的最开始:

<interceptors>
    		<!-- 声明拦截器 -->
    		<interceptor name="checkPrivilege" class="cn.itcast.oa.util.CheckPrivilegeInterceptor"></interceptor>
    		
    		<!-- 重新定义默认的拦截器栈 -->
    		<interceptor-stack name="defaultStack">
    			<interceptor-ref name="checkPrivilege"></interceptor-ref>
    			<interceptor-ref name="defaultStack"></interceptor-ref>
    		</interceptor-stack>
    	</interceptors>

       【3.3】  加载树状列表

       树状显示结构在系统的目录显示中非常常用,我们可以采用递归方式进行实现,例如下面是部门显示的树状结构:
public class departmentUtils {

	public static List<Department> getAllDepartments(List<Department> topList) {
		
		List<Department> list=new ArrayList<Department>();
		walkGetAllDepartment("┝",topList,list);
		return list;
	}
	
	public static void walkGetAllDepartment(String prox,Collection<Department> topList,List<Department> list){
		
		for(Department top:topList){
			Department department=new Department();
			department.setId(top.getId());
			department.setName(prox+top.getName());
			list.add(department);
			walkGetAllDepartment("  "+prox,top.getChildren(),list);
		}
	}

}

      但存在两个问题:

      1)在action输入多个空格,在jsp页面总是显示一个,这样使用全角空格即可解决。

      2)新添加节点后树状结构会发生乱序,这是因为在定义struts的一对多映射结构时,指定children为set属性,而set往往是无序的,解决方式:在映射文件中添加order-by属性设定,例如:order-by="id ASC"

     【3.4 】queryHelper(分页+过滤查询条件)

      分页显示在系统是经常使用的,但是如果添加搜索条件,并按照多个搜索条件进行升序或者降序排列,往往在拼接Hql语句时涉及多个if判断,这样,我们将拼接Hql语句的部分进行代码抽取,简化程序开发人员在Hql拼接方面的难度。

      
public class QueryHelper {
	
	private String fromClause; //From子句
	private String whereClause=""; //where子句
	private String orderByClause=""; //orederby子句
	
	private List<Object> parameters=new ArrayList<Object>();

	/**
	 * 生成from子句
	 * 
	 * @param clazz
	 * @param alias
	 */
	public QueryHelper(Class clazz,String alias){
		fromClause="From "+clazz.getSimpleName()+" " +alias;
	}
	
	/**
	 * 添加where子句
	 * 
	 * @param condition
	 * @param params
	 * @return
	 */
	public QueryHelper addCondition(String condition,Object... params){
		
		if(whereClause.length()==0){
			whereClause=" where "+condition;
		}else{
			whereClause+=" and "+condition;
		}
		
		if(params!=null){
			for(Object p:params){
				parameters.add(p);
			}
		}
		return this;
	}
	
	/**
	 * 如果第一个参数为true,则拼接where子句
	 * 
	 * @param append
	 * @param condition
	 * @param params
	 * @return
	 */
	public QueryHelper addCondition(boolean append,String condition,Object... params){
		
		if(append){
			addCondition(condition,params);
		}
		return this;
	}
	
	/**
	 * 添加orderBy属性
	 * 
	 * @param propertyName
	 * @param asc
	 * @return
	 */
	public QueryHelper addOrderProperty(String propertyName,boolean asc){
		if(orderByClause.length()==0){
			orderByClause=" order by "+propertyName+" "+(asc ?"ASC":"DESC");
		}else{
			orderByClause+=","+propertyName+" "+(asc? "ASC":"DESC");
		}
		return this;
	}
	
	/**
	 * 判断是否要添加orderby属性
	 * 
	 * @param append
	 * @param propertyName
	 * @param asc
	 * @return
	 */
	public QueryHelper addOrderProperty(boolean append,String propertyName,boolean asc){
		if(append){
			addOrderProperty(propertyName,asc);
		}
		return this;
	}
	
	/**
	 * 获取用于生成查询数据列表的HQL语句
	 * @return
	 */
	public String getListQueryHql(){
		return fromClause+whereClause+orderByClause;
	}
	
	/**
	 * 获取用于生成查询总记录数的HQL语句
	 * @return
	 */
	public String getCountQueryHql(){
		return "select count(*) "+fromClause+whereClause;
	}
	
	
	/**
	 * 获取参数列表
	 * 
	 * @return
	 */
	public List<Object> getParameters(){
		return parameters;
	}
	
	/**
	 * 通过prearePageBean将数据放入值栈中
	 * @param service
	 * @param pageNum
	 * @param pageSize
	 */
	public void preparePageBean(DaoSupport<?> service,int pageNum,int pageSize){
		PageBean pageBean =service.getPageBean(pageNum, pageSize, this);
		ActionContext.getContext().getValueStack().push(pageBean);
	}
}

       这样,在action中用户拼接所有可能发生的情况时,就得到了简化:

new QueryHelper(Topic.class,"t")
			//过滤条件
			.addCondition("t.forum=?", forum)  //查询的论坛实体
			.addCondition((viewtype==1),"t.type=?", Topic.TYPE_BEST)  //是否为精帖
			//排序条件
			.addOrderProperty((orderBy==1), "t.lastUpdateTime",asc)  //是否按照最后更新时间排序
			.addOrderProperty((orderBy==2), "t.postTime", asc)   //是否按照发布时间排序
			.addOrderProperty((orderBy==3),"t.replyCount" ,asc)  //是否按照回复数量排序
			.addOrderProperty((orderBy==0),"(CASE t.type WHEN 2 THEN 2 ELSE 0 END)", false)
			.addOrderProperty((orderBy==0),"t.lastUpdateTime",false)
			.preparePageBean(topicService, pageNum, pageSize);

【总结】

      在系统设计过程中,抽象是面向对象设计的第一步,将公共代码进行抽象封装,以组件化的形式存在,不但提高代码复用率,降低耦合,在业务变更时,也可以快速整理。

   



评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值