13 3月 2007

Dependency checking mechanism

Dependency checking mechanism 在使用Spring的dependency injection功能時,有不少人會希望使用constructor injection,主要是因為這樣同時可以確保所有的dependency都一定會被設定到,但我個人比較偏好使用method injection,一方面這樣比較好使用mock object來做測試,一方面我也不喜歡看到一長串難以分別順序的constructor,而且如果要新增一個subclass,那同時也要extends一個至少一樣長的Constructor... Spring forum最近發起了一個新的投票 How do you ensure required dependencies are set?,看看大家最常用的Dependency檢核機制是什麼,目前看來還是以Annotation @Required, implements InitializingBean後以assert檢查及Constructor injection三者略多,當然也有不少票是投給完全不檢查的... 因為我習慣使用method injection,所以在允許的環境下(一定要用JDK5),我也喜歡用@Required來檢查,如果環境不允許,我就會變成完全不檢查... 以spring提供的例子來看@Required的用法 在要檢查Dependency的setter method加上@Required
public class SimpleMovieLister {
 // the SimpleMovieLister has a dependency on the MovieFinder
 private MovieFinder movieFinder;
 // a setter method so that the Spring container can 'inject' a MovieFinder
 @Required
 public void setMovieFinder(MovieFinder movieFinder) {
  this.movieFinder = movieFinder;
 }
}
然後還需要在xml中加入
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
做為執行檢查@Required之用 如果原本開發系統已經有類似的Annotation,或是不想之後一定要使用Spring,可以使用自定的Annotation
package org.elliot.jdk;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Mandatory {

}
在xml只要加入像下列config即可使用自訂的Annotation達成相同的效果
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor">
 <property name="requiredAnnotationType" value="org.elliot.jdk.Mandatory"/>
</bean>

07 3月 2007

記得

是不是有這樣的一首歌,這樣的一些人, 總在人聲漸歇時在思緒裡緩緩地浮起? 我還記得 --梁靜茹 絲路 十年後的今天遇見你 年少輕狂已遠去 成熟穩重也保持距離 沉默裡千言萬語 時光回到那年夏天 公車站前你笑容滿面 拍拍我的頭 說你好嗎? 一句問候填滿青春 別人的話都聽不見 歲月凝結在你的視線 我還記得那年傾盆大雨 狼狽奔跑穿越幾條街 握緊的雙手 為愛的不顧一切 我還記得那年你的聲音 耳邊迴盪那一句誓言 你吻我的臉 都是我心中 收藏一生的快樂 漸漸我們都有新朋友 多久不再並肩走 忙忙碌碌看人生匆匆 忘記了年輕的夢 好想回到那年夏天 教室門前你笑容滿面 拍拍我的頭 說妳別哭 考壞一次不是末日 未來還有很長的路 我們要一起去看世界 我還記得那年晴空萬里 那一道飛機雲的弧線 蜿蜒著思念 寫下故事的總結 我還記得那年你的年輕 刻在從前最美的時間 在我生命裡 你不曾告別 不曾走遠 與你重逢前一個夜晚 往事在夢中上演 終要去體驗 真實人生的殘缺 我還記得那年晴空萬里 那一道飛機雲的弧線 蜿蜒著思念 寫下故事的總結 我還記得那年你的年輕 刻在從前最美的時間 在我生命裡 你不曾告別 不曾走遠

True Colors

True Colors - Cyndi Lauper You with the sad eyes don't be discouraged oh I realize it's hard to take courage in a world full of people you can lose sight of it all and the darkness inside you can make you fell so small But I see your true colors shining through I see your true colors and that's why I love you so don't be afraid to let them show your true colors true colors are beautiful like a rainbow Show me a smile then don't be unhappy, can't remember when I last saw you laughing if this world makes you crazy and you've taken all you can bear you call me up because you know I'll be there And I'll see your true colors shining through I see your true colors and that's why I love you so don't be afraid to let them show your true colors true colors are beautiful like a rainbow 這大概是我iTunes裡撥放次數最高的歌曲,間歇而有力的簡單節奏,略帶沙啞而獨特的嘶喊,鼓舞著總是略帶遲疑的我----做自己。

05 3月 2007

Struts2 Note -- (3)Action Method Invocation

Action method invocation 在Struts中使用DispatchAction及設定parameter來操做同一個Action的不同Method,可以減去不少的Action數量. 在Struts2中,可以使用wildcard來達成.如下列這個Action. MyAction.java
public class MyAction extends ActionSupport {
 public String input() {
  return "patha";
 }
 
 public String output() {
  return "pathb";
 }
}
然後在struts.xml中以"*"來代表特定組合的文字,再以"{num}"取用. struts.xml
<package name="myaction" namespace="/struts2" extends="struts-default">
 <action name="myaction_*" method="{1}" class="MyAction">
  <result name="patha">
   /jsp/inputResult.jsp
  </result>
  <result name="pathb">
   /jsp/outputResult.jsp
  </result>
 </action>
</package>
這樣的話執行 http://localhost:8080/mystruts/struts2/myaction_input.action 會呼叫MyAction.input()->patha 而下列的URL http://localhost:8080/mystruts/struts2/myaction_output.action 會呼叫MyAction.output()->pathb 看起來便利性上兩者相差不遠,不過wildcard的用途還不止如此....

Gerneric Sample

善用Generic可以減少相當多的程式碼,基本上一定會用的是在DAO及Service中 BaseDao
public interface BaseDao<T, ID extends Serializable> {
 public T get(ID id);
 
 public void save(T entity);

 public void delete(T entity);

 public List findAll(String[] ascProperties, String[] descProperties);
 
 public List findByExample(T entity, String[] ascProperties, String[] descProperties);
 
 public int countByExample(T entity);
}
HibernateBaseDao
public abstract class HibernatetBaseDao<T, ID extends Serializable> extends
  HibernateDaoSupport {

 private Class domainClass;

 public HibernatetBaseDao() {
  this.domainClass = (Class) ((ParameterizedType) getClass()
    .getGenericSuperclass()).getActualTypeArguments()[0];
 }

 public void delete(T entity) {
  this.getHibernateTemplate().delete(entity);
 }

 @SuppressWarnings("unchecked")
 public List findAll(String[] ascProperties, String[] descProperties) {
  Criteria criteria = this.getSession().createCriteria(this.domainClass);
  this.addOrderToCriteria(criteria, ascProperties, descProperties);
  return criteria.list();
 }

 @SuppressWarnings("unchecked")
 public List findByExample(T entity, String[] ascProperties,
   String[] descProperties) {
  Criteria criteria = this.getSession().createCriteria(this.domainClass);
  Example example = Example.create(entity);
  criteria.add(example);
  this.addOrderToCriteria(criteria, ascProperties, descProperties);
  return criteria.list();
 }

 @SuppressWarnings("unchecked")
 public T get(ID id) {
  return (T) this.getHibernateTemplate().get(this.domainClass, id);
 }

 public void save(T entity) {
  this.getHibernateTemplate().saveOrUpdate(entity);
 }

 public int countByExample(T entity) {
  Criteria criteria = this.getSession().createCriteria(this.domainClass);
  if (null != entity) {
   Example example = Example.create(entity);
   criteria.add(example);
  }
  return ((Integer) criteria.setProjection(Projections.rowCount()).list()
    .iterator().next()).intValue();
 }

 protected void addOrderToCriteria(Criteria criteria,
   String[] ascProperties, String[] descProperties) {
  if (null != ascProperties && 0 < ascProperties.length) {
   for (String property : ascProperties) {
    criteria.addOrder(Order.asc(property));
   }
  }
  if (null != descProperties && 0 < descProperties.length) {
   for (String property : descProperties) {
    criteria.addOrder(Order.desc(property));
   }
  }
 }

 public Class getDomainClass() {
  return domainClass;
 }

 public void setDomainClass(Class domainClass) {
  this.domainClass = domainClass;
 } 
}
BaseManager
public interface BaseManager<T, ID extends Serializable> {
 public T get(ID id);
 public void save(T entity);
 public void delete(T entity);
 public void delete(ID id);
 public List findAll();
 public List findAll(String[] ascProperties, String[] descProperties);
 public List findByExample(T entity);
 public List findByExample(T entity, String[] ascProperties, String[] descProperties);
 public int count();
 public int countByExample(T entity);
}
AbstractBaseManager
public abstract class AbstractBaseManager<T, ID extends Serializable, DAO extends BaseDao<T, ID>> {
 private DAO baseDao;

 public T get(ID id) {
  return baseDao.get(id);
 }

 public void save(T entity) {
  this.baseDao.save(entity);
 }

 public void delete(T entity) {
  this.baseDao.delete(entity);
 }

 public void delete(ID id) {
  this.baseDao.delete(this.get(id));
 }

 public List findAll() {
  return this.findAll(new String[0], new String[0]);
 }

 public List findAll(String[] ascProperties, String[] descProperties) {
  return this.baseDao.findAll(ascProperties, descProperties);
 }

 public List findByExample(T entity) {
  return this.findByExample(entity, new String[0], new String[0]);
 }

 public List findByExample(T entity, String[] ascProperties,
   String[] descProperties) {
  return this.findByExample(entity, ascProperties, descProperties);
 }

 public int count() {
  return this.countByExample(null);
 }

 public int countByExample(T entity) {
  return this.baseDao.countByExample(entity);
 }

 public DAO getBaseDao() {
  return baseDao;
 }

 public void setBaseDao(DAO baseDao) {
  this.baseDao = baseDao;
 }

}
這樣每新增一個Domain Class要做基本的CRUD時,僅需要這樣宣告
public interface MasterDao extends BaseDao<Master, String> {

}
public class MasterDaoImpl extends HibernatetBaseDao<Master, String> implements MasterDao {
 public MasterDaoImpl() {
  super();
 }
}
public interface MasterManager extends BaseManager<Master, String> {

}
public class MasterManagerImpl extends AbstractBaseManager<Master, String, MasterDao> implements
  MasterManager {

}
雖然Service與DAO間Method重覆性太高,但是維持了架構的一致性,雖然不是很滿意,還好目前使用Generic減少了重覆的Code,還算是可以接受.

03 3月 2007

Tomcat Note -- (1)

最近常用的OpenSource更新項目不少,繼Maven2、Struts2後,Tomcat也發佈了Stable的6.0.10版本,之前還是測試時,沒有特別去關心,反正5.5還是很夠用,直到6.0.10才有點興趣。 這版有些不同,Log4j已經不是預設的Log Library,若要用還請下載Source Code的Distribution,再依docs/logging.html中的說明產生需要的component。 另一點,Apache Tomcat Native library仍然未放入其中,所以需的的人除了下載APR自行compile外,也可以至HEAnet下載放入$CATALINA_HOME/bin下即可。

02 3月 2007

Struts2 Note -- (2)整合Spring2

2.0.6與之前版本整合的設定 2.0.6與之前版本的整合方式有很大不同,而整合的Spring版本也從1.2.8到了2.0.1 2.0.6將org.apache.struts2.spring.StrutsSpringObjectFactory自struts2-core移至struts2-spring-plugin, 所以必需要加入struts2-spring-plugin的Library.使用Maven2的可以這樣設定 pom.xml
<dependency>
 <groupId>org.apache.struts</groupId>
 <artifactId>struts2-spring-plugin</artifactId>
 <version>2.0.6</version>
</dependency>
2.0.6與之前的版本皆需在struts.propertis中設定
struts.objectFactory = spring
struts2-spring-plugin中除了org.apache.struts2.spring.StrutsSpringObjectFactory這個主要java外, 另外有的就是一個struts-plugin.xml,設定了struts.objectFactory真正使用的Class struts-plugin.xml
<struts>
    <bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
    
    <!--  Make the Spring object factory the automatic default -->
    <constant name="struts.objectFactory" value="spring" />

    <package name="spring-default">
        <interceptors>
            <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
            <interceptor name="sessionAutowiring" class="org.apache.struts2.spring.interceptor.SessionContextAutowiringInterceptor"/>
        </interceptors>
    </package>    
</struts>
2.0.6與之前版本整合的差異 之前的版本是這樣整合的
org.apache.struts2.dispatcher.Dispatcher
 private void init(ServletContext servletContext) {
  .......................
  if (Settings.isSet(StrutsConstants.STRUTS_OBJECTFACTORY)) {
   String className = (String) Settings.get(StrutsConstants.STRUTS_OBJECTFACTORY);
   if (className.equals("spring")) {
    className = "org.apache.struts2.spring.StrutsSpringObjectFactory";
   }
  }
  .......................
 }
很明顯的有硬來的嫌疑 2.0.6則是分為了幾個部份
org.apache.struts2.dispatcher.Dispatcher
 public void init() {
  init_DefaultProperties(); // [1]
  init_TraditionalXmlConfigurations(); // [2]
  init_LegacyStrutsProperties(); // [3]
  .......................
  init_AliasStandardObjects() ; // [4]
  .......................
 }
[1]載入了org/apache/struts2/default.properties [2]利用了StrutsXmlConfigurationProvider來載入struts-default.xml,struts-plugin.xml,struts.xml三個xml, 其中就包含了struts2-spring-plugin的struts-plugin.xml,這樣就能取得 <constant name="struts.objectFactory" value="spring" /> <bean ... name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" /> 這兩項設定 [3]載入struts.properties,透過手動設定的struts.objectFactory = spring, 就可以明白要載入的ObjectFactory Class是哪一個了 [4]此時才真正載入所有指定的Class 這樣做得確是較之前的方式好上不少,但是xml的設定檔就只能有這三個struts-default.xml,struts-plugin.xml,struts.xml 特別是struts-plugin.xml,如果自己還要寫Plugin或是希望同時能使用兩種不同的Plugin,都會遇到設定的問題.