Spring framework 用博大精深來形容應不為過。
我在學習 Spring 時,參考了滿多相關文件、書籍,以下列出我的學習心得,希望能有助 Spring framework 的同好。
*官方文件
這不是廢話,真的。
Spring 的官方文件寫得很不錯,看完後可以馬上上手,尤其是Spring 更新滿快的,某些新功能的資料可能只有官方文件才有。
有 Spring rference 2.5 中文版,雖然舊了一點,但對看中文比英文快的人來說,不失為快速上手的一條捷徑。
*Spring in Action
初見此書的時候,被其廣度嚇到了,寫這樣的一本書,到底要花多少時間呢?
這本書的好處是,前幾章基本介紹看完後,後面的章節可以挑有需要看的,不需要要全部節章都看過一遍。
文筆略閒枯燥,但技術參考書籍嘛,這不算什麼大缺點。
此書做為官方文件的補充資料很適合,我在學習的時候,好幾次遇到官方文件沒寫的,在此書上可找到;有時候官方文件有的,在此書上卻沒看到。
真不知道是有意還是巧合。
此書有簡中版本,雖然通常慢英文版一個版次,但還是那句老話,對看中文比英文快的人,是一大福音。
我只看過簡中第2版,翻得還行,第3版等入手後再補充。
*SPRING揭秘
中國人寫的,看內容可以想見作者在 spring 上花了很多功夫且是通過實戰考驗的。
可惜的是此書以spring 2為主,內容稍舊。
最大的缺點是,它絕版了。
若有機會入手的話,是一本滿不錯的參考書。
以下是不推的,原因就不多說。
*Spring Live
*Spring Recipes
2013年10月3日 星期四
2011年9月28日 星期三
Spring MVC 的怪事一椿
今天遇到怪事一椿, 其實已經困擾我一陣子了, 只是今天才發現原因所在.
Spring MVC 的 @RequestMapping 註解可用來標示 Url, 例如:
如果同時有二個相同的註解, spring 會報錯, 訊息如下:
但有一個例外, 就是 / , 一般看到網站的 / 會參考 tomcat 的 web.xml中 welcome-file 的設定.
若是下面的寫法, spring 不會報錯, welcome-file也不會生效, 但會連到那個 URL 就不知道了...
Spring MVC 的 @RequestMapping 註解可用來標示 Url, 例如:
@RequestMapping(value={"index"}, method=RequestMethod.POST)
public ModelAndView index(HttpSession session) {
...
}
如果同時有二個相同的註解, spring 會報錯, 訊息如下:
2318 [main] ERROR org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping#0': Initialization of bean failed; nested exception is java.lang.IllegalStateException: Cannot map handler 'xxxxController' to URL path [/index]: There is already handler of type [class yyyyController$$EnhancerByCGLIB$$349da4f8] mapped.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:900)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:455)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:294)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:215)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4206)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4705)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
at org.apache.catalina.core.StandardService.start(StandardService.java:525)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
但有一個例外, 就是 / , 一般看到網站的 / 會參考 tomcat 的 web.xml中 welcome-file 的設定.
若是下面的寫法, spring 不會報錯, welcome-file也不會生效, 但會連到那個 URL 就不知道了...
@RequestMapping(value={"/", "index"})
public ModelAndView index(HttpSession session) {
...
}
@RequestMapping(value={"/", "login"})
public ModelAndView index(HttpSession session) {
...
}
@RequestMapping(value={"/", "login"})
public ModelAndView login(HttpSession session) {
...
}
2011年9月5日 星期一
tomcat jsessionid url problem
Spring MVC 可以 cache static content, 例如圖檔, js, css...
參考文件
但我之前遇到一個問題, 就是原本應顯示為 /appbased/static-1.0.0/images/xxx 的 URL
在第一次執行時, URL 卻會變成 /appbased/static-1.0.0?jsessionid=481AFDE46F818357ED2096824F250AFF/images/xxx
需重新 reload 一次才回復正常.
當時我的作法是, 在首次登入頁面的 jsp 前面加一行
但最近又遇到另一個問題, 就是首頁有可能是登入狀態, 也有可能是未登入狀態, 總不能老是把 session 抓出來幹掉裝作沒事, 故查了一下解法.
參考文件
但我之前遇到一個問題, 就是原本應顯示為 /appbased/static-1.0.0/images/xxx 的 URL
在第一次執行時, URL 卻會變成 /appbased/static-1.0.0?jsessionid=481AFDE46F818357ED2096824F250AFF/images/xxx
需重新 reload 一次才回復正常.
當時我的作法是, 在首次登入頁面的 jsp 前面加一行
<% request.getSession(true).invalidate(); %>
即可.但最近又遇到另一個問題, 就是首頁有可能是登入狀態, 也有可能是未登入狀態, 總不能老是把 session 抓出來幹掉裝作沒事, 故查了一下解法.
2011年8月8日 星期一
Spring internationalization (i18n) 學習筆記
Spring 提供了下列幾種 MessageSource 給 i18n 用.
StaticMessageSource
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
使用及設定上3者都差不多, ReloadableResourceBundleMessageSource 的設定檔不需要放在 classpath 內, 且可以指定 cache, 故我選擇它實作.
實作環境Spring 3.1.0M1
StaticMessageSource
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
使用及設定上3者都差不多, ReloadableResourceBundleMessageSource 的設定檔不需要放在 classpath 內, 且可以指定 cache, 故我選擇它實作.
實作環境Spring 3.1.0M1
2011年7月22日 星期五
Spring Transcation 學習筆記
參考來源: Transaction Management
* 使用預設的Proxy mode時, @Transactional 只能用在 public method 上. 除非改用 AspectJ mode.
* Spring 不建議 @Transactional 用於 interface 上. (除非你使用 interface-based proxy )
* 預設只有在發生 Unchecked Exception 時才會自動 rollback, 可以指定Exception來Rollback.
ex @Transactional(rollbackFor=Exception.class)
* TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 可以手動rollback
* eclipse 似乎無法 auto import Annotation 中的變數, 當使用 Propagation 時會出現 Propagation cannot be resolved to a variable 的錯誤, 手動 import org.springframework.transaction.annotation.Propagation; 即可.
* 使用預設的Proxy mode時, @Transactional 只能用在 public method 上. 除非改用 AspectJ mode.
* Spring 不建議 @Transactional 用於 interface 上. (除非你使用 interface-based proxy )
* 預設只有在發生 Unchecked Exception 時才會自動 rollback, 可以指定Exception來Rollback.
ex @Transactional(rollbackFor=Exception.class)
* TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 可以手動rollback
* eclipse 似乎無法 auto import Annotation 中的變數, 當使用 Propagation 時會出現 Propagation cannot be resolved to a variable 的錯誤, 手動 import org.springframework.transaction.annotation.Propagation; 即可.
2011年6月20日 星期一
Spring + Proxool 注意
今天把 tomcat 6 的 jdk 從 1.5 換成 1.6 後, 怪事發生了.
一直出現 java.sql.SQLException: org.logicalcobwebs.proxool.ProxoolException: Attempt to refer to a unregistered pool by its alias 'proxool_pool'
這問題之前遇過, 是因為 web.xml 中 servlet load-on-startup 順序沒設定好造成的.
怎麼只換了個 jdk 又沒改 xml 卻又發生了?
一直出現 java.sql.SQLException: org.logicalcobwebs.proxool.ProxoolException: Attempt to refer to a unregistered pool by its alias 'proxool_pool'
這問題之前遇過, 是因為 web.xml 中 servlet load-on-startup 順序沒設定好造成的.
<servlet>
<servlet-name>proxoolServletConfigurator</servlet-name>
<servlet-class>org.logicalcobwebs.proxool.configuration.ServletConfigurator</servlet-class>
<init-param>
<param-name>xmlFile</param-name>
<param-value>WEB-INF/config/proxool.xml</param-value>
</init-param>
<param-value>WEB-INF/config/proxool.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
怎麼只換了個 jdk 又沒改 xml 卻又發生了?
2011年6月15日 星期三
如何在新增資料後取得自動遞增的主鍵值? How to retrieve the auto-generated database key
自動遞增(ex: mysql 的 auto increment)的主鍵(Primary key)值, 如何在新增(insert)一筆資料後, 取得剛新增的資料鍵值呢?
Spring 的 NamedParameterJdbcTemplate 的 int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder) , 可於新增後取得鍵值.
這裡有範例.
Spring 的 NamedParameterJdbcTemplate 的 int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder) , 可於新增後取得鍵值.
這裡有範例.
2011年6月10日 星期五
Spring 3 + 檔案下載 (file download / export file)
網路上看到的文件都是在講怎麼處理 pdf or excel 的檔案,
但如果是要給使用者下載 jpg or txt 檔該怎麼做呢?
今天試了一下, 其實還滿簡單的. 繼承 AbstractView 即可.
下面是示範的程式碼:
但如果是要給使用者下載 jpg or txt 檔該怎麼做呢?
今天試了一下, 其實還滿簡單的. 繼承 AbstractView 即可.
下面是示範的程式碼:
2011年6月9日 星期四
Spring + Json
為了使用Json, 今天在spring xml 加入下面的設定,
卻出現 exception 如下
是因為少了Jackson Library的關係, 可按此下載
Json 簡單的範例如下:
Java Code
@ResponseBody會依據收到的的 Accept Header 自動將回傳物件轉為客戶端可接受的型態.
此例會將 Java 物件轉為 Json 物件.
Spring 內建了數種轉換物件, 詳情可參考 Spring 說明文件
以下為回傳HashMap的範例:
Java Code:
html code (via jQuery):
ContentNegotiatingViewResolver 會依據
1. URL 的結尾(extension)
2. Accept header
3. 以上都沒有辦法判斷的話, 使用 defaultContentType
來判斷 request 的要求是那一種型態.
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="text/xml" />
<entry key="htm" value="text/html" />
<entry key="html" value="text/html" />
</map>
</property>
<property name="defaultContentType" value="text/html" />
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="requestContextAttribute"><value>rc</value></property>
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<entry key="json" value="application/json" />
<entry key="xml" value="text/xml" />
<entry key="htm" value="text/html" />
<entry key="html" value="text/html" />
</map>
</property>
<property name="defaultContentType" value="text/html" />
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="requestContextAttribute"><value>rc</value></property>
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
</bean>
卻出現 exception 如下
Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.view.ContentNegotiatingViewResolver#0' defined in ServletContext resource [/WEB-INF/config/webmvc-config.xml]: Cannot create inner bean 'org.springframework.web.servlet.view.json.MappingJacksonJsonView#17b0998' of type [org.springframework.web.servlet.view.json.MappingJacksonJsonView] while setting bean property 'defaultViews' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.view.json.MappingJacksonJsonView#17b0998' defined in ServletContext resource [/WEB-INF/config/webmvc-config.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.web.servlet.view.json.MappingJacksonJsonView]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/codehaus/jackson/map/ObjectMapper
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.view.json.MappingJacksonJsonView#17b0998' defined in ServletContext resource [/WEB-INF/config/webmvc-config.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.web.servlet.view.json.MappingJacksonJsonView]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/codehaus/jackson/map/ObjectMapper
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.view.json.MappingJacksonJsonView#17b0998' defined in ServletContext resource [/WEB-INF/config/webmvc-config.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.web.servlet.view.json.MappingJacksonJsonView]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/codehaus/jackson/map/ObjectMapper
是因為少了Jackson Library的關係, 可按此下載
Json 簡單的範例如下:
Java Code
@RequestMapping(value={"JsonTester"})
@ResponseBody
public ObjectNode jsonTester() {
ObjectNode rootNode = new ObjectMapper().createObjectNode();
rootNode.put("result", true);
return rootNode ;
}
html code(via jQuery)
$(document).ready(function() {
$.post("JsonTester.json",function(data) {
if (data.result) {
alert("成功");
} else {
alert("失敗");
}
});
});
return rootNode ;
}
html code(via jQuery)
$(document).ready(function() {
$.post("JsonTester.json",function(data) {
if (data.result) {
alert("成功");
} else {
alert("失敗");
}
});
});
@ResponseBody會依據收到的的 Accept Header 自動將回傳物件轉為客戶端可接受的型態.
此例會將 Java 物件轉為 Json 物件.
Spring 內建了數種轉換物件, 詳情可參考 Spring 說明文件
以下為回傳HashMap的範例:
Java Code:
@RequestMapping(value={"GetMap"})
@ResponseBody
public ObjectNode getMap(String inputValue) {
ObjectNode rootNode = new ObjectMapper().createObjectNode();
HashMap<String, String> result = myService.getMap(inputValue);
rootNode.put("result", true);
rootNode.putPOJO("myMap", result);
return rootNode ;
}
@ResponseBody
public ObjectNode getMap(String inputValue) {
ObjectNode rootNode = new ObjectMapper().createObjectNode();
HashMap<String, String> result = myService.getMap(inputValue);
rootNode.put("result", true);
rootNode.putPOJO("myMap", result);
return rootNode ;
}
html code (via jQuery):
$.getJSON("GetMap.json", {inputValue: variable }, function(data) {
if (data.result) {
$.each(data.myMap, function(key, value){
alert(key + "-" + value) ;
});
} else {
alert("Get Map failed");
}
})
if (data.result) {
$.each(data.myMap, function(key, value){
alert(key + "-" + value) ;
});
} else {
alert("Get Map failed");
}
})
ContentNegotiatingViewResolver 會依據
1. URL 的結尾(extension)
2. Accept header
3. 以上都沒有辦法判斷的話, 使用 defaultContentType
來判斷 request 的要求是那一種型態.
2011年5月25日 星期三
Spring 3 SimpleJdbcDaoSupport
網路上似乎沒看到用Annotation 又 extends SimpleJdbcDaoSupport的, 花了我一個下午的時間測試.
Spring 3 中如果要用DaoSupport的類別, 二種作法如下:
(奇怪的是, Spring Framework Reference Documentation完全沒提DaoSupport)
Annotation的作法如下:
Spring 3 中如果要用DaoSupport的類別, 二種作法如下:
(奇怪的是, Spring Framework Reference Documentation完全沒提DaoSupport)
Annotation的作法如下:
2011年5月21日 星期六
2011年5月19日 星期四
Spring + GWT 學習筆記
第一次嘗試 Spring MVC程式, 在動手前已先看完 Spring in Action 2nd 以及 Spring in Action 3rd (Early access edition).
但啟動過程中卻不順利, 在此紀錄一下遇到的問題及解決方法.
Environment:
Windows 7 32bit
JDK 1.5.0.22
Tomcat 6.0.32
Spring 3.1.0.M1 (w/ webflow 2.3.0)
GWT 2.3.0
但啟動過程中卻不順利, 在此紀錄一下遇到的問題及解決方法.
Environment:
Windows 7 32bit
JDK 1.5.0.22
Tomcat 6.0.32
Spring 3.1.0.M1 (w/ webflow 2.3.0)
GWT 2.3.0
標籤:
Development,
GWT,
Spring
訂閱:
文章 (Atom)