关于ssh开发的一点小结
废话 start
从学习ssh以来,忙碌了那么久,做了那么多实践开发项目,一直想总结一下,一直没时间和心思写,现在趁着上班打酱油的时间简单的总结一下吧!
他们说,跨的太大了就容易扯蛋!
废话 end
Struts2:
一, struts.xml配置最简化
namespace:是指访问路径的命名空间,默认情况是项目根路径。例如项目名称为news
配置如下:
<package name="default"namespace="/pages" extends="struts-default">
<!-- 通过URL访问的路径是 /namesapce/Entity/method.do -->
<action name="addNew" method="addNew">
<result>/jsp/list.jsp</result>
</action>
</package>
则访问URL为:https://xxx.xxx.xx:xx/news/pages/addNew.do
亦如配置:
<package name="default"namespace="/pages/news" extends="struts-default">
<!-- 通过URL访问的路径是 /namesapce/Entity/method.do -->
<action name="addNew" method="addNew">
<result>/jsp/list.jsp</result>
</action>
</package>
则访问URL为:https://xxx.xxx.xx:xx/news/pages/news/addNew.do
虽然访问路径不一样但是请求的方法其实是一样的
通配符使用:使用通配符*可以使得配置十分简化
如配置:
<package name="default"namespace="/pages " extends="struts-default">
<!-- 通过URL访问的路径是 /namesapce/Entity/method.do -->
<action name="add_*" method="add{1}">
<result>/jsp/list_{1}.jsp</result>
</action>
</package>
“*”代表所有,{1}代表第一个“*”,这样就可以节省配置,但是必须约定好命名规则。
视图结果定义到Action中:
无论是在编写代码的时候还是在读代码的时候总会又到这样的烦恼,在action和strut文件之间来回切换,影响思维连贯性。所有试图将strut.xml的配置如下:
<package name="default" namespace="/pages" extends="struts-default">
<!-- 通过URL访问的路径是 /namesapce/Entity/method.do -->
<action name="*/*" method="{2}">
<result>${result}</result>
</action>
</package>
${result}是action实例成员变量
Action学增加成员变量 result 并提供get和set方法
例如
public String result = "success";
public String getResult(){
return result;
}
public void setResult(String result) {
this.result = result;
}
public String addNews() throws Exception {
result = “/jsp/list_news.jsp”;
return super.execute();
}
这样struts.xml只需一次性配置好,以后只要按照规范命名就可以把注意力放在actionz中而不用不再理strut.xml了,也不会影响项目的可读性
二, 参数接受及类型转换
Struts2剪掉了struts1的actionForm,剪掉了actionForm与Pojo的冗余
其实不论是struts1的actionForm封装参数还是Struts2的直接用属性提供set方法接受参数都需要对参数进行类型转换,一般用的BeanUtils,而通常情况先Struts2模式是使用ognl进行类型转换的,定义在action中的非复合类型参数还可以,可是遇到复合类型接受参数的时候就容易出现问题,比如时间类型。如果你做个ssh的开发你应该都会在lib中看到这样的一个jar:
是apache下面的一个包,是一个常用的在对象之间复制数据和数据类型转换的工具类, struts就是依赖于它进行ActionForm的创建和类型的转换的。无论是struts1的actionform还是struts2的pojo接收参数,大概都会有这样的一个过程, org.apache.commons.beanutils.BeanUtils和org.apache.commons.beanutils.converters包中有一系列converter类是最用的,BeanUtils主要用于对象属性之间的复制,依赖于其对类型的识别,和转换。无论是struts1的actionForm的还是Struts2的pojo接收参数,大概都有这样的一个过程:1解析request获取参数,2
创建 actionForm或者pojo对象,3 org.apache.commons.beanutils.converters对参数进行类型识别和抓获,4 属性复制。前提是我们提前对类型转换器进行注册。
类型转换器的注册:
在可以再服务器启动时进行注册,注册之后,类型转换器就存在内存中,等待需要的时候调用
static{
ConvertUtils.register(new StringConverter(), String.class);
ConvertUtils.register(new StringConverter(),String.class);
//date
ConvertUtils.register(new DateConverter(null),java.util.Date.class);
ConvertUtils.register(newSqlDateConverter(null),java.sql.Date.class);
ConvertUtils.register(new SqlTimeConverter(null),Time.class);
ConvertUtils.register(newSqlTimestampConverter(null),Timestamp.class);
//number
ConvertUtils.register(new BooleanConverter(null), Boolean.class);
ConvertUtils.register(new ShortConverter(null), Short.class);
ConvertUtils.register(new IntegerConverter(null), Integer.class);
ConvertUtils.register(new LongConverter(null), Long.class);
ConvertUtils.register(new FloatConverter(null), Float.class);
ConvertUtils.register(new DoubleConverter(null), Double.class);
ConvertUtils.register(newBigDecimalConverter(null), BigDecimal.class);
ConvertUtils.register(newBigIntegerConverter(null),BigInteger.class);
}
三, 关于requestAware、sessionAware、applicationAware
Aware,意识的意思,request意识,session意识…
requestAware、sessionAware、applicationAware是封装在strut2的核心包里面的几个接口,
Struts2解除了与actionServlet的耦合,但很多时候我们还是会用的request,session这几个对象的,为此根据需要我们可以选择性的实现requestAware、sessionAware、applicationAware这几个接口,当然我们用的最多的还是requestAware接口;例如:
public abstract class BaseStruts2Action extends ActionSupport implementsRequestAware {
protected Map requestMap = null;
public String result = "success";
static {
//注册converters
ConvertRegisterHelper.registerConverters();
}
//实现requestAware的方法
@Override
public void setRequest(Map request) {
this.requestMap = request;
}
public String delete(){
request.put("message", "删除成功");
return "/commons/message.jsp";
}
}
Action中实现RequestAware,SessionAware,ApplicationAware接口,
实现这些接口,都会有相对应的setXXX()方法.就是说谁来执行这个action中的相应方法,谁就对这些个对象进行初始化(Spring中的注入).也就是Struts2为我们进行了初始化,所以这三个值都不需要自己初始化. 通过request.put("message", "删除成功")就相当于办消息存到了request的Attribut里面了,在message.jsp里面可以通过${message}取到值。
Hibernate 3.2
一, ORM, 选择annotation
最初接触hibernate的时候进行or映射的时候都是通过xml,文件来映射的,一个pojo对应一个xml文件。Hibernate3.2提供了强大的注解机制,觉得通过annotation的方式进行or映射,就得代码可变得更高,提高内聚性,一不用在pojo和xml文件直接来回切换。例如:
@Entity
@Table(name = "tb_gate")
public class TbGate implements java.io.Serializable{
private static final long serialVersionUID = 5454155825314635342L;
@Length(max=30)
private java.lang.String gateModeid;
private java.lang.Integer gateId;
//columns END
public TbGate(){
}
public TbGate(
java.lang.Integer gateId
){
this.gateId = gateId;
}
public void setGateId(java.lang.Integer value) {
this.gateId = value;
}
@Id @GeneratedValue(generator="custom-id")
@GenericGenerator(name="custom-id", strategy = "assigned")
@Column(name = "gate_id", unique = true, nullable = false, insertable = true, updatable = true, length = 10)
public java.lang.Integer getGateId() {
return this.gateId;
}
@Column(name = "gate_modeid", unique = false, nullable = true, insertable = true, updatable = true, length = 30)
public java.lang.String getGateModeid() {
return this.gateModeid;
}
public void setGateModeid(java.lang.String value) {
this.gateModeid = value;
}
private TbMode tbMode;
public void setTbMode(TbMode tbMode){
this.tbMode = tbMode;
}
@ManyToOne(cascade = {}, fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "gate_modeid",nullable = false, insertable = false, updatable = false)
})
public TbMode getTbMode() {
return tbMode;
}
}
通过注解分别指明了映射的表名称,字段名称关联等信息。在sessionfactory的配置中指明映射的pojo:
- -- --
<!-- 映射持久化类-->
<mapping class="cn.serup.model.TbGate " />
</session-factory>
</hibernate-configuration>
二, QBC(Query By Criteria)
最初用hibernate做项目的时候多是使用的hql语句进行对象检索,后来在一个团队项目中看到同事代码使用了Criteria,看着挺陌生的,但是代码挺简洁的,百度了一下才知道这就是着名的QBC检索方式, Hibernate中共提供了三种检索方式:HQL(Hibernate Query Language)、QBC、QBE(QueryBy Example)。Criteria提供了灵活的查询条件的组装。Hibernate 设计了 CriteriaSpecification 作为 Criteria
的父接口,下面提供了 Criteria和DetachedCriteria 。
Criteria 和 DetachedCriteria 的主要区别在于创建的形式不一样, Criteria 是在线的,所以它是由 Hibernate Session 进行创建的;而 DetachedCriteria 是离线的,创建时无需 Session,DetachedCriteria 提供了 2 个静态方法 forClass(Class) 或 forEntityName(Name)
进行DetachedCriteria 实例的创建。 Spring 的框架提供了getHibernateTemplate
().findByCriteria(detachedCriteria) 方法可以很方便地根据DetachedCriteria 来返回查询结果。Criteria 和 DetachedCriteria 均可使用 Criterion 和 Projection 设置查询条件。可以设置 FetchMode( 联合查询抓取的模式 ) ,设置排序方式。对于 Criteria 还可以设置 FlushModel (冲刷 Session 的方式)和 LockMode (数据库锁模式)。 下面对 Criterion 和 Projection
进行详细说明。 Criterion 是 Criteria 的查询条件。Criteria 提供了 add(Criterion criterion) 方法来添加查询条件。 Criterion 接口的主要实现包括: Example 、 Junction 和 SimpleExpression 。而 Junction 的实际使用是它的两个子类 conjunction 和 disjunction ,分别是使用 AND 和 OR 操作符进行来联结查询条件集合。 Criterion 的实例可以通过 Restrictions
工具类来创建,Restrictions 提供了大量的静态方法,如 eq (等于)、 ge (大于等于)、 between 等来方法的创建 Criterion 查询条件 (SimpleExpression 实例)。除此之外, Restrictions 还提供了方法来创建 conjunction 和 disjunction 实例,通过往该实例的 add(Criteria) 方法来增加查询条件形成一个查询条件集合,
例如:
DetachedCriteria criteria = DetachedCriteria.forClass(Channel.class);
criteria.add(Restrictions.ne("wrap", 0));
criteria.add(Restrictions.eq("categoryId", 1));
criteria.add(Restrictions.like("name", "%频道%"));
List l = getHibernateTemplate().findByCriteria(criteria,(curPage - 1) * pageSize,pageSize);
Spring
使用注解,简化配置
基于注释(Annotation)的配置有越来越流行的趋势,Spring 2.5以后 顺应这种趋势,提供了完全基于注释配置 Bean、装配 Bean 的功能,您可以使用基于注释的 Spring IoC 替换原来基于 XML 的配置。本文通过实例详细讲述了 Spring 2.5 基于注释 IoC 功能的使用,使用Annotation后我们可以把spring的配置简化到极致,几乎之用一行配置代码就看代替原来的所有
例如配置:
<!-- component-scan自动搜索@Component , @Controller , @Service , @Repository等标注的类 -->
<context:component-scan base-package="cn.**.dao"/>
<context:component-scanbase-package="cn.**.service" />
这样spring,在服务器启动的时候,spring就自动到匹配cn.**.dao和cn.**.service的中扫描发现有@Repository, @Component, @Controller , @Service , @Repository等标准的类就将其加入到ioc容器中
如:
@Repository
public class AdminDao {
@SuppressWarnings("unchecked")
public void method(String adminName) {
}
@Transactional
public class AdminService implements IAdminService {
@Resource
private AdminDao adminDao;
public Admin findByAdminName(String adminName) {
// TODO Auto-generatedmethod stub
return this.adminDao.findByAdminName(adminName);
}
}
Spring会根据@Repository和@Transactional注解将AdminDao和AdminService装配到bean工厂中(默认情况下id为类名的首字母小写),并通过@Resource标注将adminDao注入给adminService
三大框架ssh
一、 jsp层设计
文件结构:
良好的jsp文件结构的设计在一定程度也可简化设计,增强内聚性:
如图,把一下常用的公用的写到一个文件,在别的文件include进来即可
例如:
Taglibs.jsp
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ taglib uri="https://java.sun.com/jsp/jstl/core"prefix="c" %>
<%@ taglib uri="https://java.sun.com/jsp/jstl/fmt"prefix="fmt" %>
<%@ taglib uri="https://java.sun.com/jsp/jstl/functions"prefix="fn" %>
<%@ taglib uri="/struts-tags"prefix="s" %>
<c:set var="ctx" value="${pageContext.request.contextPath}"/>
meta.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ include %>
<!-- 引入公共CSS和JS -->
<scripttype="text/javascript"src="${ctx}/js/jquery.min.js"></script>
<script type="text/javascript" src="${ctx}/js/page.js"></script>
<link href="${ctx}/css/reset.css" rel="stylesheet" type="text/css" />
<link href=${ctx}/css/page.css" rel="stylesheet" type="text/css" />
在其他页面中只需
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ include %>
<head>
<title>游戏参数信息</title>
<%@ include %>
</head>
加入红色部分即可。
二、 Action
提取重复动作和公用属性
在做项目的时候常常会发现有些动作常常在每个action中的不断的重复出现,例如对request等web元素的获取,返回结构的定义等等,所以应该把公用的动作或者属性提取出来写到一个共同的父类中,如:
public abstract class BaseAction extends ActionSupport implements RequestAware {
protected Map requestMap = null;
public String result = "success";
public voidcopyProperties(Object target,Object source) throws IllegalAccessException,InvocationTargetException {
BeanUtils.copyProperties(target, source);
}
public void setRequest(Map request) {
this.requestMap = request;
}
public HttpServletRequest getRequest() {
return ServletActionContext.getRequest();
}
public HttpServletResponse getResponse() {
return ServletActionContext.getResponse();
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
实现Preparable,ModelDriven接口
通常情况下在action里面少不了对一个pojo的增删查该等操作,无论是那种操作,都少不了对这个pojo的初始化这个步骤,也就是领域对象。 Strut2提供了Preparable接口,该接口提供了一个Preparable()方法,实现了该接口的action,执行方法之前都会先执行Preparable()方法,于是我们就可以再Preparable()方法中做领域对象的初始化了。
实现了modelDriven接口可以在action中直接获得例如User对象,它会将Object getModel()取得的User放到ValueStack中。可以理解为将这个User的属性追加到Action中。它主要是作用是实现类似Struts的FormBean功能。
在struts2中,提供了一种直接使用领域对象的方式,就是让action实现com.opensymphony.xwork2.ModelDriven接口,ModelDriven让你可以直接操作应用程序中的领域对象,允许你在web层和业务层使用相同的对象。
ModelDriven接口只有一个方法
public Object getModel() {
return user;
}
该方法返回一个用于接收用户输入数据的对象模型,在这个模型对象中的属性可以直接通过(属性名)userName来访问,而不需要使用(对象名.属 性名)user.userName这种格式来访问了,在action也不需要对对象提供getter和setter方法了。
Preparable、ModelDriven这两个接口通常是结合起来使用的,但是这里有个问题:默认的拦截器栈会先执行Preparable,再执行parma参数的获取,使得我们的领域模型不能正常初始化,我们想要的顺序应该是:先执行parma参数的获取,再执行Preparable方法,怎么办?我们可以改变连接器栈的顺序,strut提供了<interceptor-refname="paramsPrepareParamsStack"/>这个拦截器栈,从paramsPrepareParamsStack这个名字就可以知道提提供给我们的顺序,先prams参数获取再执行Prepare,最后将Param参数保存到valueStacks中。例如:
Action中代码:
public class AdminAction extends BaseAction implements Preparable,ModelDriven{
private AdminManager adminManager;
private Admin admin;
java.lang.Integer id = null;
public void prepare() throws Exception {
if (id== null) {
admin = new Admin();
} else {
admin = (Admin)adminManager.getById(id);
}
}
public void setAdminManager(AdminManager manager) {
this.adminManager = manager;
}
public Object getModel() {
return admin;
}
public void setAdminId(java.lang.Integer val) {
this.id = val;
}
public String execute()throws Exception{
this.result = “success.jsp”;
return super.execute();
}
}
Struts.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//ApacheSoftware Foundation//DTD Struts Configuration 2.0//EN"
"https://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode"value="true" />
<constant name="struts.enable.SlashesInActionNames"value="true" />
<package name="custom-default" extends="struts-default">
<interceptors>
<interceptor-stack name="customDefaultCrudStack">
<interceptor-refname="paramsPrepareParamsStack"/>
</interceptor-stack>
</interceptors>
<package name="itime" namespace="/pages"extends=" customDefaultCrudStack ">
<action name="*/*"method="{2}">
<result>${result}</result>
</action>
</package>
<package name="terminal" namespace="/terminal"extends="struts-default">
<!-- 通过URL访问的路径是 /namesapce/Entity/method.do -->
<action name="*/*"method="{2}" >
<result>${result}</result>
</action>
</package>
</struts>
AdminAction在响应的时候:1 先获取adminId,如果有则id=adminId,没有则id=null;2 再执行Preprare方法,根据id是否为空分别对admin进行两种方式初始化;3 然后再获取admin属性对应的相关参数,有则执行admin里面的setXxx()方法。4 响应结束即excute方法处理完成之后 getModel()方法执行,将admin返回放入值栈中。
三、 Service、DAO
很多时候,我们搭建ssh项目框架的时候,在做service层和dao层的都会用一个借口一个实现类,一对一的形式,如:
刚开始学习做ssh项目的时候也是照着这么做的,也没去问个究竟,还来看了rapid-framWork的项目,有看了有位仁兄的一篇文章,叫做《关于接口的滥用问题》,感觉写得颇有道理。
仔细观察一下,所以的service或者dao接口定义的方法都差不多,无法就是一些增删改查的方法,只是操作的对象不一样而已,那么何不把对象抽象一下,所有的接口写成一个接口,在写一个实现这个接口的抽象类,这样不减少接口的数量,增强了内聚性,还提供的代码的复用率,dao接口及抽象类写一次就可以再整个项目中使用。在这里泛型得到了很好的应用,例如:
统一的dao接口,E,entity的抽象,pk,identity的抽象。
public interface EntityDao<E,PK extends Serializable>{
public Object getById(PK id) throws DataAccessException;
public void deleteById(PK id) throws DataAccessException;
/** 插入数据 */
public void save(E entity) throws DataAccessException;
/** 更新数据 */
public void update(E entity) throws DataAccessException;
/** 根据id检查是否插入或是更新数据 */
public void saveOrUpdate(E entity) throws DataAccessException;
public boolean isUnique(E entity, String uniquePropertyNames) throwsDataAccessException;
/** 用于hibernate.flush() 有些dao实现不需要实现此类 */
public void flush() throws DataAccessException;
public List<E> findAll() throws DataAccessException;
}
统一的dao抽象类,E,entity的抽象,pk,identity的抽象。
public abstract class BaseHibernateDao<E,PK extends Serializable> extendsHibernateDaoSupport implements EntityDao<E,PK>{
public abstract Class getEntityClass();
public void save(E entity) {
getHibernateTemplate().save(entity);
}
/*查询所以对象*/
public List<E> findAll() {
return getHibernateTemplate().loadAll(getEntityClass());
}
/*根据id查询对象*/
public E getById(PK id) {
return (E)getHibernateTemplate().get(getEntityClass(),id);
}
/*删除对象*/
public void delete(Objectentity) {
getHibernateTemplate().delete(entity);
}
public void delete(Serializable entity) {
getHibernateTemplate().delete(entity);
}
/*根据id删除对象*/
public void deleteById(PK id) {
Object entity = getById(id);
if(entity == null) {
throw new ObjectRetrievalFailureException(getEntityClass(),id);
}
getHibernateTemplate().delete(entity);
}
/*更新对象*/
public void update(E entity) {
getHibernateTemplate().update(entity);
}
/*更新或保存对象*/
public void saveOrUpdate(E entity) {
getHibernateTemplate().saveOrUpdate(entity);
}
具体的对象的Dao,E明朗化
@Repository
public class AdminDao extends BaseHibernateDao<Admin,java.lang.Integer>{
public Class getEntityClass() {
return Admin.class;
}
}
Servic的结构和dao的结构也差不多,省去了接口,一个抽象类就ok了
例如:
统一的service抽象类,E,entity的抽象,pk,identity的抽象
@Transactional
public abstract class BaseService <E,PK extends Serializable>{
protected Log log = LogFactory.getLog(getClass());
protected abstract EntityDao getEntityDao();
@Transactional(readOnly=true)
public E getById(PK id) throws DataAccessException{
return (E)getEntityDao().getById(id);
}
@Transactional(readOnly=true)
public List<E> findAll() throws DataAccessException{
return getEntityDao().findAll();
}
public void saveOrUpdate(E entity) throws DataAccessException{
getEntityDao().saveOrUpdate(entity);
}
public void save(E entity) throws DataAccessException{
getEntityDao().save(entity);
}
public void removeById(PK id) throws DataAccessException{
getEntityDao().deleteById(id);
}
public void update(E entity) throws DataAccessException{
getEntityDao().update(entity);
}
@Transactional(readOnly=true)
public boolean isUnique(E entity, String uniquePropertyNames) throwsDataAccessException {
return getEntityDao().isUnique(entity,uniquePropertyNames);
}
}
具体的对象的servic,E明朗化
@Service
@Transactional
public class AdminServic extends BaseService<Admin,java.lang.Integer>{
private AdminDao adminDao;
public void setAdminDao(AdminDao dao) {
this.adminDao = dao;
}
public EntityDao getEntityDao() {
return this.adminDao;
}
}
最后更新:2017-04-02 06:51:53