11.2 实现通用层
11.2.1 功能概述
通过抽象通用的功能,从而复用,减少重复工作:
- 对于一些通用的常量使用一个专门的常量类进行定义;
- 对于视图分页,也应该抽象出来,如JSP做出JSP标签;
- 通用的数据层代码,如通用的CRUD,减少重复劳动,节约时间;
- 通用的业务逻辑层代码,如通用的CRUD,减少重复劳动,节约时间;
- 通用的表现层代码,同样用于减少重复,并提供更好的代码结构规范。
11.2.2 通用的常量定义
目标:在一个常量类中定义通用的常量的好处是如果需要修改这些常量值只需在一个地方修改即可,变的地方只有一处而不是多处。
如默认分页大小如果在多处硬编码定义为10,突然发生变故需要将默认分页大小10为5,怎么办?如果当初我们提取出来放在一个通用的常量类中是不是只有一处变动。
java代码:
- package cn.javass.commons;
- public class Constants {
- public static final int DEFAULT_PAGE_SIZE = 5;
- public static final String DEFAULT_PAGE_NAME = "page";
- public static final String CONTEXT_PATH = "ctx";
- }
-
如上代码定义了通用的常量,如默认分页大小。
11.2.2通用分页功能
分页功能是项目中必不可少的功能,因此通用分页功能十分有必要,有了通用的分页功能,即有了规范,从而保证视图页面的干净并节约了开发时间。
1、 分页对象定义,用于存放是否有上一页或下一页、当前页记录、当前页码、分页上下文,该对象是分页中必不可少对象,一般在业务逻辑层组装Page对象,然后传送到表现层展示,然后通用的分页标签使用该对象来决定如何显示分页:
java代码:
- package cn.javass.commons.pagination;
- import java.util.Collections;
- import java.util.List;
- public class Page<E> {
- private boolean hasPre;
- private boolean hasNext;
- private List<E> items;
- private int index;
-
- public int getIndex() {
- return this.index;
- }
- public boolean isHasPre() {
- return this.hasPre;
- }
- public boolean isHasNext() {
- return this.hasNext;
- }
- public List<E> getItems() {
- return this.items == null ? Collections.<E>emptyList() : this.items;
- }
- }
-
2、 分页标签实现,将使用Page对象数据决定如何展示分页,如图11-9和11-10所示:
图11-9 11-10 通用分页标签实现
图11-9和11-10展示了两种分页展示策略,由于分页标签和集成SSH没多大关系且不是必须的并由于篇幅问题不再列出分页标签源代码,有兴趣的朋友请参考cn.javass.commons.pagination.NavigationTag类文件。代码下载地址
11.2.3 通用数据访问层
目标:通过抽象实现最基本的CURD操作,提高代码复用,可变部分按需实现。
1、通用数据访问层接口定义
java代码:
- package cn.javass.commons.dao;
- import java.io.Serializable;
- import java.util.List;
- public interface IBaseDao<M extends Serializable, PK extends Serializable> {
- public void save(M model);
- public void saveOrUpdate(M model);
- public void update(M model);
- public void merge(M model);
- public void delete(PK id);
- public M get(PK id);
- public int countAll();
- public List<M> listAll();
- public List<M> listAll(int pn, int pageSize);
- }
-
通用DAO接口定义了如CRUD通用操作,而可变的(如查询所有已发布的接口,即有条件查询等)需要在相应DAO接口中定义,并通过泛型“M”指定数据模型类型和“PK”指定数据模型主键类型。
2、通用数据访问层DAO实现
此处使用Hibernate实现,即实现是可变的,对业务逻辑层只提供面向接口编程,从而隐藏数据访问层实现细节。
实现时首先通过反射获取数据模型类型信息,并根据这些信息获取Hibernate对应的数据模型的实体名,再根据实体名组装出通用的查询和统计记录的HQL,从而达到同样目的。
注意我们为什么把实现生成HQL时放到init方法中而不是构造器中呢?因为SessionFactory是通过setter注入,setter注入晚于构造器注入,因此在构造器中使用SessionFactory会是null,因此放到init方法中,并在Spring配置文件中指定初始化方法为init来完成生成HQL。
java代码:
- package cn.javass.commons.dao.hibernate;
-
- public abstract class BaseHibernateDao<M extends Serializable, PK extends Serializable> extends HibernateDaoSupport implements IBaseDao<M, PK> {
- private Class<M> entityClass;
- private String HQL_LIST_ALL;
- private String HQL_COUNT_ALL;
- @SuppressWarnings("unchecked")
- public void init() {
-
- this.entityClass = (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
-
- String entityName = getSessionFactory().getClassMetadata(this.entityClass).getEntityName();
-
- HQL_LIST_ALL = "from " + entityName;
- HQL_COUNT_ALL = " select count(*) from " + entityName;
- }
- protected String getListAllHql() {
- return HQL_LIST_ALL;
- }
- protected String getCountAllHql() {
- return HQL_COUNT_ALL;
- }
- public void save(M model) {
- getHibernateTemplate().save(model);
- }
- public void saveOrUpdate(M model) {
- getHibernateTemplate().saveOrUpdate(model);
- }
- public void update(M model) {
- getHibernateTemplate().update(model);
- }
-
- public void merge(M model) {
- getHibernateTemplate().merge(model);
- }
- public void delete(PK id) {
- getHibernateTemplate().delete(this.get(id));
- }
- public M get(PK id) {
- return getHibernateTemplate().get(this.entityClass, id);
- }
- public int countAll() {
- Number total = unique(getCountAllHql());
- return total.intValue();
- }
- public List<M> listAll() {
- return list(getListAllHql());
- }
- public List<M> listAll(int pn, int pageSize) {
- return list(getListAllHql(), pn, pageSize);
- }
- protected <T> List<T> list(final String hql, final Object... paramlist) {
- return list(hql, -1, -1, paramlist);
- }
-
-
-
-
-
-
-
-
- @SuppressWarnings("unchecked")
- protected <T> List<T> list(final String hql, final int pn, final int pageSize, final Object... paramlist) {
- return getHibernateTemplate().
- executeFind(new HibernateCallback<List<T>>() {
- public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
- Query query = session.createQuery(hql);
- if (paramlist != null) {
- for (int i = 0; i < paramlist.length; i++) {
- query.setParameter(i, paramlist[i]);
- }
- }
- if (pn > -1 && pageSize > -1) {
- query.setMaxResults(pageSize);
- int start = PageUtil.getPageStart(pn, pageSize);
- if(start != 0) {
- query.setFirstResult(start);
- }
- }
- return query.list();
- }
- });
- }
-
-
-
-
-
- @SuppressWarnings("unchecked")
- protected <T> T unique(final String hql, final Object... paramlist) {
- return getHibernateTemplate().execute(new HibernateCallback<T>() {
- public T doInHibernate(Session session) throws HibernateException, SQLException {
- Query query = session.createQuery(hql);
- if (paramlist != null) {
- for (int i = 0; i < paramlist.length; i++) {
- query.setParameter(i, paramlist[i]);
- }
- }
- return (T) query.setMaxResults(1).uniqueResult();
- }
- });
- }
-
- }
-
通用DAO实现代码相当长,但麻烦一次,以后有了这套通用代码将会让工作很轻松,该通用DAO还有其他便利方法因为本示例不需要且由于篇幅原因没加上,请参考源代码。
11.2.4 通用业务逻辑层
目标:实现通用的业务逻辑操作,将常用操作封装提高复用,可变部分同样按需实现。
1、通用业务逻辑层接口定义
java代码:
- package cn.javass.commons.service;
-
- public interface IBaseService<M extends Serializable, PK extends Serializable> {
- public M save(M model);
- public void saveOrUpdate(M model);
- public void update(M model);
- public void merge(M model);
- public void delete(PK id);
- public M get(PK id);
- public int countAll();
- public List<M> listAll();
- public Page<M> listAll(int pn);
- public Page<M> listAll(int pn, int pageSize);
- }
3、 通用业务逻辑层接口实现
通用业务逻辑层通过将通用的持久化操作委托给DAO层来实现通用的数据模型CRUD等操作。
通过通用的setDao方法注入通用DAO实现,在各Service实现时可以通过强制转型获取各转型后的DAO。
java代码:
- package cn.javass.commons.service.impl;
-
- public abstract class BaseServiceImpl<M extends Serializable, PK extends Serializable> implements IBaseService<M, PK> {
- protected IBaseDao<M, PK> dao;
- public void setDao(IBaseDao<M, PK> dao) {
- this.dao = dao;
- }
- public IBaseDao<M, PK> getDao() {
- return this.dao;
- }
- public M save(M model) {
- getDao().save(model);
- return model;
- }
- public void merge(M model) {
- getDao().merge(model);
- }
- public void saveOrUpdate(M model) {
- getDao().saveOrUpdate(model);
- }
- public void update(M model) {
- getDao().update(model);
- }
- public void delete(PK id) {
- getDao().delete(id);
- }
- public void deleteObject(M model) {
- getDao().deleteObject(model);
- }
- public M get(PK id) {
- return getDao().get(id);
- }
- public int countAll() {
- return getDao().countAll();
- }
- public List<M> listAll() {
- return getDao().listAll();
- }
- public Page<M> listAll(int pn) {
- return this.listAll(pn, Constants.DEFAULT_PAGE_SIZE);
- }
- public Page<M> listAll(int pn, int pageSize) {
- Integer count = countAll();
- List<M> items = getDao().listAll(pn, pageSize);
- return PageUtil.getPage(count, pn, items, pageSize);
- }
- }
-
11.2.6通用表现层
目标:规约化常见请求和响应操作,将常见的CURD规约化,采用规约编程提供开发效率,减少重复劳动。
Struts2常见规约编程:
- 通用字段驱动注入:如分页字段一般使用“pn”或“page”来指定当前分页页码参数名,通过Struts2的字段驱动注入实现分页页码获取的通用化;
- 通用Result:对于CURD操作完全可以提取公共的Result名字,然后在Strust2配置文件中进行规约配置;
- 数据模型属性名:在页面展示中,如新增和修改需要向值栈或请求中设置数据模型,在此我们定义统一的数据模型名如“model”,这样在项目组中形成约定,大家只要按照约定来能提高开发效率;
- 分页对象属性名:与数据模型属性名同理,在此我们指定为“page”;
- 便利方法:如获取值栈、请求等可以提供公司内部需要的便利方法。
1、通用表现层Action实现:
java代码:
- package cn.javass.commons.web.action;
- import cn.javass.commons.Constants;
-
- public class BaseAction extends ActionSupport {
-
- public static final String LIST = "list";
- public static final String REDIRECT = "redirect";
- public static final String ADD = "add";
-
- public static final String MODEL = "model";
-
- public static final String PAGE = Constants.DEFAULT_PAGE_NAME;
- public static final int DEFAULT_PAGE_SIZE = Constants.DEFAULT_PAGE_SIZE;
- private int pn = 1;
-
- public ActionContext getActionContext() {
- return ActionContext.getContext();
- }
- public ValueStack getValueStack() {
- return getActionContext().getValueStack();
- }
- }
2、通用表现层JSP视图实现:
将视图展示的通用部分抽象出来减少页面设计的工作量。
2.1、通用JSP页头文件(WEB-INF/jsp/common/inc/header.jsp):
此处实现比较简单,实际中可能包含如菜单等信息,对于可变部分使用请求参数来获取,从而保证了可变与不可变分离,如标题使用“${param.title}”来获取。
java代码:
- <%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
- <!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=UTF-8">
- <title>${param.title}</title>
- </head>
- <body>
-
2.2、通用JSP页尾文件(WEB-INF/jsp/common/inc/footer.jsp):
此处比较简单,实际中可能包含公司版权等信息。
2.3、通用JSP标签定义文件(WEB-INF/jsp/common/inc/tld.jsp):
在一处定义所有标签,避免标签定义使代码变得凌乱,且如果有多个页面需要新增或删除标签即费事又费力。
java代码:
- <%@taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
- <%@taglib prefix="s" uri="/struts-tags" %
2.4、通用错误JSP文件(WEB-INF/jsp/common/error.jsp):
当系统遇到错误或异常时应该跳到该页面来显示统一的错误信息并可能在该页保存异常信息。
java代码:
- <%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
- <jsp:include page="inc/header.jsp"/>
- 失败或遇到异常!
- <jsp:include page="inc/footer.jsp"/>
2.5、通用正确JSP文件(WEB-INF/jsp/common/success.jsp):
对于执行成功的操作可以使用通用的页面表示,可变部分同样可以使用可变的请求参数传入。
java代码:
- <%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
- <jsp:include page="inc/header.jsp"/>
- 成功!
- <jsp:include page="inc/footer.jsp"/>
3、通用设置web环境上下文路径拦截器:
用于设置当前web项目的上下文路径,即可以在JSP页面使用“${ctx}”获取当前上下文路径。
java代码:
- package cn.javass.commons.web.filter;
-
-
- public class ContextPathFilter implements Filter {
- @Override
- public void init(FilterConfig config) throws ServletException {
- }
- @Override
- public void doFilter(
- ServletRequest request, ServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- String contextPath = ((HttpServletRequest) request).getContextPath();
- request.setAttribute(Constants.CONTEXT_PATH, contextPath);
- chain.doFilter(request, response);
- }
- @Override
- public void destroy() {
- }
- }
11.2.7通用配置文件
目标:通用化某些常用且不可变的配置文件,同样目标是提高复用,减少工作量。
1、Spring资源配置文件(resources/applicationContext-resources.xml):
定义如配置元数据替换Bean、数据源Bean等通用的Bean。
java代码:
- <bean class=
- "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="locations">
- <list>
- <value>classpath:resources.properties</value>
- </list>
- </property>
- </bean>
- <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
- <property name="targetDataSource">
- <bean class="org.logicalcobwebs.proxool.ProxoolDataSource">
- <property name="driver" value="${db.driver.class}" />
- <property name="driverUrl" value="${db.url}" />
- <property name="user" value="${db.username}" />
- <property name="password" value="${db.password}" />
- <property name="maximumConnectionCount" value="${proxool.maxConnCount}" />
- <property name="minimumConnectionCount" value="${proxool.minConnCount}" />
- <property name="statistics" value="${proxool.statistics}" />
- <property name="simultaneousBuildThrottle" value="${proxool.simultaneousBuildThrottle}" />
- <property name="trace" value="${proxool.trace}" />
- </bean>
- </property>
- </bean>
- </beans>
通过通用化如数据源来提高复用,对可变的如数据库驱动、URL、用户名等采用替换配置元数据形式进行配置,具体配置含义请参考【7.5集成Spring JDBC及最佳实践】。
2、替换配置元数据的资源文件(resources/resources.properties):
定义替换配置元数据键值对用于替换Spring配置文件中可变的配置元数据。
java代码:
- #数据库连接池属性
- proxool.maxConnCount=10
- proxool.minConnCount=5
- proxool.statistics=1m,15m,1h,1d
- proxool.simultaneousBuildThrottle=30
- proxool.trace=false
- db.driver.class=com.mysql.jdbc.Driver
- db.url=jdbc:mysql:
- db.username=root
- db.password=
3、通用Struts2配置文件(WEB-INF/struts.xml):
由于是要集成Spring,因此需要使用StrutsSpringObjectFactory,我们需要在action名字中出现“/”因此定义struts.enable.SlashesInActionNames=true。
在此还定义了“custom-default”包继承struts-default包,且是抽象的,在包里定义了如全局结果集全局异常映射。
java代码:
- <?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.objectFactory" value="org.apache.struts2.spring.StrutsSpringObjectFactory"/>
- <!-- 允许action的名字中出现"/" -->
- <constant name="struts.enable.SlashesInActionNames" value="true"/>
- <package name="custom-default" extends="struts-default" abstract="true">
- <global-results>
- <result name="success">/WEB-INF/jsp/common/success.jsp</result>
- <result name="error">/WEB-INF/jsp/common/error.jsp</result>
- <result name="exception">/WEB-INF/jsp/common/error.jsp</result>
- </global-results>
- <global-exception-mappings>
- <exception-mapping result="exception" exception="java.lang.Exception"/>
- </global-exception-mappings>
- </package>
- </struts>
4、通用log4j日志记录配置文件(resources/log4j.xml):
可以配置基本的log4j配置文件然后在其他地方通过拷贝来定制需要的日志记录配置。
java代码:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
- <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
- <!-- Appenders -->
- <appender name="console" class="org.apache.log4j.ConsoleAppender">
- <param name="Target" value="System.out" />
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%-5p: %c - %m%n" />
- </layout>
- </appender>
- <!-- Root Logger -->
- <root>
- <priority value="DEBUG" />
- <appender-ref ref="console" />
- </root>
- </log4j:configuration>
4、通用web.xml配置文件定义(WEB-INF/web.xml):
定义如通用的集成配置、设置web环境上下文过滤器、字符过滤器(防止乱码)、通用的Web框架拦截器(如Struts2的)等等,从而可以通过拷贝复用。
java代码:
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app id="WebApp_ID" version="2.5" 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">
- <!-- 通用配置开始 -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:applicationContext-resources.xml
- </param-value>
- </context-param>
- <listener>
- <listener-class>
- org.springframework.web.context.ContextLoaderListener
- </listener-class>
- </listener>
- <!-- 通用配置结束 -->
- <!-- 设置web环境上下文(方便JSP页面获取)开始 -->
- <filter>
- <filter-name>Set Context Path</filter-name>
- <filter-class>cn.javass.commons.web.filter.ContextPathFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>Set Context Path</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- 设置web环境上下文(方便JSP页面获取)结束 -->
- <!-- 字符编码过滤器(防止乱码)开始 -->
- <filter>
- <filter-name>Set Character Encoding</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>
- <init-param>
- <param-name>forceEncoding</param-name>
- <param-value>true</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>Set Character Encoding</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- 字符编码过滤器(防止乱码)结束 -->
- <!-- Struts2.x前端控制器配置开始 -->
- <filter>
- <filter-name>struts2Filter</filter-name>
- <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
- </filter>
-
- <filter-mapping>
- <filter-name>struts2Filter</filter-name>
- <url-pattern>*.action</url-pattern>
- </filter-mapping>
- <!-- Struts2.x前端控制器配置结束 -->
- </web-app>