<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>tcp://192.168.3.48:61616</value>
</property>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref bean="connectionFactory"/>
</property>
</bean>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0">
<value>HelloWorldQueue</value>
</constructor-arg>
</bean>
</beans>
2.寫發(fā)送方
package ch13.JMS;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
public class HelloWorldSender
{
public static void main(String args[]) throws Exception
{
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "ch13/JMS/applicationContext.xml" });
JmsTemplate jmsTemplate = (JmsTemplate) context.getBean("jmsTemplate");
Destination destination = (Destination) context.getBean("destination");
jmsTemplate.send
(
destination, new MessageCreator()
{
public Message createMessage(Session session) throws JMSException
{
return session.createTextMessage("大家好這個是測試!");
}
}
);
}
}
3.寫接收方
package ch13.JMS;
import javax.jms.Destination;
import javax.jms.TextMessage;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
public class HelloWorldReciver
{
public static void main(String args[]) throws Exception
{
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "ch13/JMS/applicationContext.xml" });
JmsTemplate jmsTemplate = (JmsTemplate) context.getBean("jmsTemplate");
Destination destination = (Destination) context.getBean("destination");
System.out.println("will wait:" + jmsTemplate.getReceiveTimeout()+ " seconds for message");
TextMessage msg = (TextMessage) jmsTemplate.receive(destination);
System.out.println("reviced msg is:" + msg.getText());
}
}
4.啟動activemq中bin 下的activemq.bat
5.先運行 HelloWorldSender
6.再運行 HelloWorld
7.結(jié)果:
will wait:-1 seconds for message
reviced msg is:大家好 這個是測試!
4.5 Copy Task:對文件和目錄進行復制Copy 任務把一個或多個文件復制到指定的目錄下。但要注意的是,如果目標目錄下具有同名的文件,那么只有當源文件相對于目標文件更新時,Ant工具才會復制這個文件。在Copy任務中可以使用FileSet類型來定義文件集合。 4.5.1 Copy Task的屬性及功能Copy 任務具有以下屬性: (1)file:用于指定要復制的源文件。 (2)preservelastmodified:作用是使得復制后的文件與源文件的最后修改時間相同。默認為false。 (3)tofile:用于指定要復制到的文件。 (4)todir:用于指定要復制到的目標目錄。todir和tofile只能使用其中一個屬性。 (5)overwrite:用于指定是否需要覆蓋目錄和文件,不管文件是否比源文件新,都會覆蓋。默認為false。 (6)filtering:用于指定在復制時是否使用構(gòu)件文件的全局過濾器對文件進行過濾。默認為false。 (7)flatten:用于指定是否需要復制目錄,如果為true代表把所有的文件復制到todir屬性設定的目錄下。默認為false,復制目錄。 (8)includeEmptyDirs:用于指定是否復制空目錄。默認為true。 (9)failonerror:用于指定當遇到錯誤時是否停止執(zhí)行。默認為true。 (10)verbose:用于指定當復制文件時是否記錄日志信息。 (11)encoding:用于設定復制文件時的編碼或文件過濾器使用的編碼方式。默認時使用Java虛擬機的編碼方式。 (12)outputencoding:指定寫文件時的編碼方式。默認時使用Java虛擬機的編碼方式。 (13)enablemultiplemappings:用于設定是否允許多個映射。默認為false。 (14)granularity:用于指定一個文件修改時間的毫秒數(shù)據(jù)的允許誤差。因為不是所有的文件系統(tǒng)的修改時間都是精確到毫秒數(shù)。默認時為0,如果為DOS系統(tǒng)則為2。 4.5.2 通過Copy Task實現(xiàn)文件和目錄復制功能實例不管是對程序進行打包還是一般的文件操作,基本上都離不開復制功能。通過Ant工具的Copy任務可以讓程序在Windows和Linux/UNIX下對文件和目錄進行復制操作。例如: (1)對單個文件進行復制: <copy file="myfile.txt" tofile="mycopy.txt"/> 這個例子的作用是在當前目錄復制myfile.txt,并把復制的文件命名為mycopy.txt。當需要把文件復制到別外的目錄時可以這樣編寫: <copy file="myfile.txt" todir="../some/other/dir"/> 這個例子的作用是把文件復制到與當前目錄同級的some目錄的/other/dir子目錄下。這里“..”代表相對路徑(當前目錄的上一級目錄)。 (2)對文件目錄進行復制: <copy todir="../new/dir"> <fileset dir="src_dir"/> </copy> 這個例子的作用是把src_dir目錄復制到../new/dir目錄下。有時對文件進行復制時需要對文件進行備份。下面舉一個復制文件時對文件進行備份的例子。 <copy todir="../backup/dir"> <fileset dir="src_dir"> <exclude name="**/*.java"/> </fileset> <globmapper from="*" to="*.bak"/> </copy> 這個例子的作用是把src_dir目錄及其子目錄下所有非Java文件復制到../backup/dir目錄下,并重命名為bak文件,以作備份。 4.5.3 在執(zhí)行Copy Task時使用文件過濾的實例下面是一個復制文件的同時替換文件中的特殊符號的例子: <copy todir="../backup/dir"> <fileset dir="src_dir"/> <filterset> <filter token="TITLE" value="Foo Bar"/> </filterset> </copy> 這個例子的作用是把src_dir目錄下的所有文件復制到../backup/dir目錄,并在所有文件中查找并替換@TITLE@為Foo Bar。當要進行新產(chǎn)品發(fā)布時通過需要替換文件中的版本信息和時間信息。 說明:在Ant工具中還提供了copydir和copyfile這兩個特定的任務,分別用于對目錄和文件進行復制。但自從有了Copy任務后,這兩個任務已過期,不建議再使用,應該統(tǒng)一使用Copy任務。 4.6 Delete Task:對文件和目錄進行刪除Delete 任務可用于刪除一個或多個文件,或刪除一個或多個目錄和目錄下的文件。默認時不會刪除空目錄,要刪除空目錄可以設定includeEmptyDirs屬性為true。在Delete任務中可以使用FileSet和DirSet類型。 4.6.1 Delete Task的屬性及功能Delete任務包括以下屬性: (1)file:用于指定要刪除的文件的名稱,可以為相對路徑或絕對路徑。 (2)dir:指定一個將要被刪除的根目錄。這個目錄下的子目錄及文件將可能被刪除。dir屬性和file屬性兩者必須指定其一。 (3)verbose:作用是指定是否在命令行中輸出被刪除的文件的名稱。默認為false。 (4)quiet:作用是指定當要被刪除的文件或目錄不存在時是否不顯示提示信息。默認為false,代表要顯示提示信息。 (5)failonerror:用于指定當出現(xiàn)錯誤時是否停止執(zhí)行命令。 (6)includeemptydirs:表明當使用FileSet類型時是否刪除空的目錄。 (7)includes:用于指定將要刪除的文件或目錄的模式。可用逗號或空格符進行分隔。 (8)includesfile:用于指定要刪除的文件的模式。已不建議使用。 (9)excludes:用于指定一個或多個文件模式或目錄模式。這些符合條件的文件和目錄將不被刪除。 (10)excludesfile:用于指定將不被刪除的文件模式,已不建議使用。 (11)defaultexcludes:用于指定是否使用Ant默認的default excludes模式,已不建議使用。 (12)deleteonexit:用于指定是否采用Java File類中的deleteOnExit()方法進行判斷,如果使用這個方法,那么僅當存在文件時才進行刪除,默認取值為false。 4.6.2 在執(zhí)行Delete Task時使用文件過濾的實例(1)刪除單個文件的實例: <delete file="/lib/ant.jar"/> 這個例子的作用是刪除lib目錄下的ant.jar文件。 (2)刪除目錄的實例: <delete dir="lib"/> 這個例子的作用是刪除lib目錄,包括這個目錄的所有子目錄及文件,可以通過include或exclude類型指定刪除的部分文件,而不是目錄下的所有文件。 (3)刪除所有備份文件和空的目錄的例子: <delete includeEmptyDirs="true"> <fileset dir="." includes="**/*.bak"/> </delete> 這個例子的作用是刪除當前目錄以及其子目錄下的所有.bak文件,同時也刪除所有空的目錄。 |
看到了關于Spring提供的Open Session In View的一篇文章,很詳細的介紹了相關細節(jié)。下面的文章轉(zhuǎn)載自:
【http://blog.sina.com.cn/s/blog_5dc12c490100crr5.html】
在沒有使用Spring提供的Open Session In View情況下,因需要在service(or Dao)層里把session關閉,所以lazy loading 為true的話,要在應用層內(nèi)把關系集合都初始化,如 company.getEmployees(),否則Hibernate拋session already closed Exception; Open Session In View提供了一種簡便的方法,較好地解決了lazy loading問題.
它有兩種配置方式OpenSessionInViewInterceptor和OpenSessionInViewFilter(具體參看SpringSide),功能相同,只是一個在web.xml配置,另一個在application.xml配置而已。
Open Session In View在request把session綁定到當前thread期間一直保持hibernate session在open狀態(tài),使session在request的整個期間都可以使用,如在View層里PO也可以lazy loading數(shù)據(jù),如 ${ company.employees }。當View 層邏輯完成后,才會通過Filter的doFilter方法或Interceptor的postHandle方法自動關閉session。
OpenSessionInViewInterceptor配置
<beans>
<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">
..
</property>
</bean>
</beans>
OpenSessionInViewFilter配置
<web-app>
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
<!-- singleSession默認為true,若設為false則等于沒用OpenSessionInView -->
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
</web-app>
很多人在使用OpenSessionInView過程中提及一個錯誤:
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations
are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into
FlushMode.AUTO or remove 'readOnly' marker from transaction definition
看看OpenSessionInViewFilter里的幾個方法
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,FilterChain filterChain)
throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory();
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(
sessionFactory, new SessionHolder(session));
try {
filterChain.doFilter(request, response);
}
finally {
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}
}
protected Session getSession(SessionFactory sessionFactory)
throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
session.setFlushMode(FlushMode.NEVER);
return session;
}
protected void closeSession(Session session, SessionFactory sessionFactory)
throws CleanupFailureDataAccessException {
SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);
}
可以看到OpenSessionInViewFilter在getSession的時候,會把獲取回來的session的flush mode 設為FlushMode.NEVER。然后把該sessionFactory綁定到 TransactionSynchronizationManager,使request的整個過程都使用同一個session,在請求過后再接除該 sessionFactory的綁定,最后closeSessionIfNecessary根據(jù)該 session是否已和transaction綁定來決定是否關閉session。在這個過程中,若HibernateTemplate 發(fā)現(xiàn)自當前session有不是readOnly的transaction,就會獲取到FlushMode.AUTO Session,使方法擁有寫權(quán)限。
public static void closeSessionIfNecessary(Session session, SessionFactory sessionFactory)
throws CleanupFailureDataAccessException {
if (session == null ||
TransactionSynchronizationManager.hasResource(sessionFactory)) {
return;
}
logger.debug("Closing Hibernate session");
try {
session.close();
}
catch (JDBCException ex) {
// SQLException underneath
throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex.getSQLException());
}catch (HibernateException ex) {
throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex);
}
}
也即是,如果有不是readOnly的transaction就可以由Flush.NEVER轉(zhuǎn)為Flush.AUTO,擁有 insert,update,delete操作權(quán)限,如果沒有transaction,并且沒有另外人為地設flush model的話,則doFilter的整個過程都是Flush.NEVER。所以受transaction保護的方法有寫權(quán)限,沒受保護的則沒有。
采用spring的事務聲明,使方法受transaction控制
<bean id="baseTransaction"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="proxyTargetClass" value="true"/>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="userService" parent="baseTransaction">
<property name="target">
<bean class="com.phopesoft.security.service.impl.UserServiceImpl"/>
</property>
</bean>
對 于上例,則以save,add,update,remove開頭的方法擁有可寫的事務,如果當前有某個方法,如命名為importExcel(),則因沒 有transaction而沒有寫權(quán)限,這時若方法內(nèi)有insert,update,delete操作的話,則需要手動設置flush model為Flush.AUTO,如
session.setFlushMode(FlushMode.AUTO);
session.save(user);
session.flush();
盡 管Open Session In View看起來還不錯,其實副作用不少。看回上面OpenSessionInViewFilter的doFilterInternal方法代碼,這個方法 實際上是被父類的doFilter調(diào)用的,因此,我們可以大約了解的OpenSessionInViewFilter調(diào)用流程: request(請求)->open session并開始transaction->controller->View(Jsp)->結(jié)束transaction并 close session.
一切看起來很正確,尤其是在本地開發(fā)測試的時候沒出現(xiàn)問題,但試想下如果流程中的某一步被阻塞的話,那在這期間connection就一直被占用而不釋 放。最有可能被阻塞的就是在寫Jsp這步,一方面可能是頁面內(nèi)容大,response.write的時間長,另一方面可能是網(wǎng)速慢,服務器與用戶間傳輸時 間久。當大量這樣的情況出現(xiàn)時,就有連接池連接不足,造成頁面假死現(xiàn)象。
Open Session In View是個雙刃劍,放在公網(wǎng)上內(nèi)容多流量大的網(wǎng)站請慎用。
下午發(fā)現(xiàn)的問題,晚上我把userDao和userService的定義移到了dataAccessContext-jdbc.xml后,事物又有效了。看來網(wǎng)友的分析是對的,事物沒有啟動是因為bean的加載的原因。但是這樣的話感覺不是很爽了,一部分是xml文件,一部分是注釋。繼續(xù)找好的辦法搞定這個問題。
<bean id="userDao" class="com.example.springdemo.dao.impl.UserDAOImpl" />
<bean id="userService" class="com.example.springdemo.service.impl.UserServiceImpl" />
至于java類中還是保持@Autowired的注釋。