361
人物
關於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