升級(jí)Spring3.1RC2 和Hibernate4.0.0CR7遇到的一些問(wèn)題及解決
Spring3.1RC2支持
1. Quartz2
2. Hibernate4,
3. New HandlerMethod-based Support Classes For Annotated Controller Processing
4. Consumes and Produces @RequestMapping Conditions
5. Working With URI Template Variables In Controller Methods
6. Validation For @RequestBody Method Arguments //and so on....
7. Spring MVC 3.1 的annotation可以參看下http://starscream.iteye.com/blog/1098880
Hibernate 4可以查看http://community.jboss.org/wiki/HibernateCoreMigrationGuide40
下面主要說(shuō)一下我在升級(jí)過(guò)程中遇到的一些問(wèn)題及解決辦法。
l Maven的repository始終無(wú)法升級(jí)到SpringRC2,可能服務(wù)器有問(wèn)題吧,目前暫時(shí)是從官方下載的整個(gè)SpringRC2的zip包。版本號(hào)是:3.1.0.RC2
l Hibernate可以從repository中升級(jí)到4.0.0.CR7,新增的依賴包有jandex-1.0.3.Final.jar,jboss-logging-3.1.0.CR2.jar,jboss-transaction-api_1.1_spec-1.0.0.Final.jar。
l Quartz升級(jí)到2.1.1,Ehcache-core升級(jí)到2.5.0
l Spring3.1取消了HibernateTemplate,因?yàn)镠ibernate4的事務(wù)管理已經(jīng)很好了,不用Spring再擴(kuò)展了。所以以前的Dao需要改寫,直接調(diào)用Hibernate 的Session進(jìn)行持久化。
l Spring的配置:
sessionFactory從org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean換成org.springframework.orm.hibernate4.LocalSessionFactoryBean
l Spring的配置:
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>改為
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
EhCacheRegionFactory使用配置:
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
l 使用Hibernate所有的openSession()改為getCurrentSession()
l Spring 的配置:Hibernate transactionManager從3改為4,如下:
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
l Spring @ResponseBody輸出是亂碼的問(wèn)題:原來(lái)使用的是:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
改為:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name = "messageConverters">
<list>
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name = "supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class = "org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name = "supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
這樣比每個(gè)Controller都加上@RequestMapping(value = "/showLeft", method = RequestMethod.GET)
produces = "text/plain; charset=utf-8"方便的多。
l Blob,以前配置:
@TypeDefs({@TypeDef(name="clob",typeClass=ClobStringType.class),@TypeDef(name="blob",typeClass=BlobByteArrayType.class)})
@Lob
@Type(type="blob")
public byte[] getPic() {
return pic;
}
現(xiàn)在改為:
@Lob
public byte[] getPic() {
return pic;
}
簡(jiǎn)單很多。
l 待續(xù)。。。
Shiro權(quán)限框架
開(kāi)發(fā)系統(tǒng)中,少不了權(quán)限,目前java里的權(quán)限框架有SpringSecurity和Shiro(以前叫做jsecurity),對(duì)于SpringSecurity:功能太過(guò)強(qiáng)大以至于功能比較分散,使用起來(lái)也比較復(fù)雜,跟Spring結(jié)合的比較好。對(duì)于初學(xué)Spring Security者來(lái)說(shuō),曲線還是較大,需要深入學(xué)習(xí)其源碼和框架,配置起來(lái)也需要費(fèi)比較大的力氣,擴(kuò)展性也不是特別強(qiáng)。
對(duì)于新秀Shiro來(lái)說(shuō),好評(píng)還是比較多的,使用起來(lái)比較簡(jiǎn)單,功能也足夠強(qiáng)大,擴(kuò)展性也較好。聽(tīng)說(shuō)連Spring的官方都不用Spring Security,用的是Shiro,足見(jiàn)Shiro的優(yōu)秀。網(wǎng)上找到兩篇介紹:http://www.infoq.com/cn/articles/apache-shiro http://www.ibm.com/developerworks/cn/opensource/os-cn-shiro/,官網(wǎng)http://shiro.apache.org/ ,使用和配置起來(lái)還是比較簡(jiǎn)單。下面只是簡(jiǎn)單介紹下我們是如何配置和使用Shiro的(暫時(shí)只用到了Shiro的一部分,沒(méi)有配置shiro.ini文件)。
首先是添加過(guò)濾器,在web.xml中:
<filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
權(quán)限的認(rèn)證類:
public class ShiroDbRealm extends AuthorizingRealm { @Inject private UserService userService ;
/** * 認(rèn)證回調(diào)函數(shù),登錄時(shí)調(diào)用. */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) UsernamePasswordToken token = (UsernamePasswordToken) authcToken; User user= userService.getUserByUserId(token.getUsername()); if (user!= null) { return new SimpleAuthenticationInfo(user.getUserId(), user.getUserId(), getName()); } else { return null; } } /** * 授權(quán)查詢回調(diào)函數(shù), 進(jìn)行鑒權(quán)但緩存中無(wú)用戶的授權(quán)信息時(shí)調(diào)用. */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String loginName = (String) principals.fromRealm(getName()).iterator().next(); User user= userService.getUserByUserId(loginName); if (user != null) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermission("common-user"); return info; } else { return null; } } } |
Spring的配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans > <description>Shiro Configuration</description> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="shiroDbRealm" /> </bean> <bean id="shiroDbRealm" class="com.company.service.common.shiro.ShiroDbRealm" /> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/common/security/login" /> <property name="successUrl" value="/common/security/welcome" /> <property name="unauthorizedUrl" value="/common/security/unauthorized"/> <property name="filterChainDefinitions"> <value> /resources/** = anon /manageUsers = perms[user:manage] /** = authc </value> </property> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> </beans> |
登錄的Controller:
@Controller @RequestMapping(value = "/common/security/*") public class SecurityController { @Inject private UserService userService; @RequestMapping(value = "/login") public String login(String loginName, String password, User user = userService.getUserByLogin(loginName); if (null != user) { setLogin(loginInfoVO.getUserId(), loginInfoVO.getUserId()); return "redirect:/common/security/welcome"; } else { return "redirect:/common/path?path=showLogin"; } }; public static final void setLogin(String userId, String password) { Subject currentUser = SecurityUtils.getSubject(); if (!currentUser.isAuthenticated()) { //collect user principals and credentials in a gui specific manner //such as username/password html form, X509 certificate, OpenID, etc. //We'll use the username/password example here since it is the most common. //(do you know what movie this is from? ;) UsernamePasswordToken token = new UsernamePasswordToken(userId, password); //this is all you have to do to support 'remember me' (no config - built in!): token.setRememberMe(true); currentUser.login(token); } };
@RequestMapping(value="/logout") @ResponseBody public void logout(HttpServletRequest request){ Subject subject = SecurityUtils.getSubject(); if (subject != null) { subject.logout(); } request.getSession().invalidate(); }; } |
注冊(cè)和獲取當(dāng)前登錄用戶:
public static final void setCurrentUser(User user) { Subject currentUser = SecurityUtils.getSubject(); if (null != currentUser) { Session session = currentUser.getSession(); if (null != session) { session.setAttribute(Constants.CURRENT_USER, user); } } }; public static final User getCurrentUser() { Subject currentUser = SecurityUtils.getSubject(); if (null != currentUser) { Session session = currentUser.getSession(); if (null != session) { User user = (User) session.getAttribute(Constants.CURRENT_USER); if(null != user){ return user; } } } }; |
需要的jar包有3個(gè):shiro-core.jar,shiro-spring.jar,shiro-web.jar。感覺(jué)shiro用起來(lái)比SpringSecurity簡(jiǎn)單很多。
在我們開(kāi)發(fā)的一個(gè)系統(tǒng)中,有定時(shí)任務(wù),自然就想到了Quartz,由于框架采用的Spring,Quartz跟Spring的集成也非常簡(jiǎn)單,所以就把Quartz配置到框架中,當(dāng)系統(tǒng)啟動(dòng)后,定時(shí)任務(wù)也就自動(dòng)啟動(dòng)。在開(kāi)發(fā)的過(guò)程中一直沒(méi)有發(fā)現(xiàn)問(wèn)題,但是最后上線的時(shí)候,采用的是weblogic cluster,啟動(dòng)了4個(gè)節(jié)點(diǎn),發(fā)現(xiàn)有的定時(shí)任務(wù)執(zhí)行了不止一次,才恍然大悟,4個(gè)節(jié)點(diǎn)啟動(dòng)了4個(gè)應(yīng)用,也就啟動(dòng)了4個(gè)定時(shí)任務(wù),所以在同一個(gè)時(shí)間定時(shí)任務(wù)執(zhí)行了不止一次。去網(wǎng)上搜索,發(fā)現(xiàn)Quartz也支持cluster,但是我覺(jué)得就我們的系統(tǒng)而言,沒(méi)有必要采用cluster的定時(shí)任務(wù),也許是比較懶吧,就想讓定時(shí)任務(wù)只執(zhí)行一次。在網(wǎng)上搜到了robbin的一篇文章(http://robbin.iteye.com/blog/40989 ),發(fā)現(xiàn)把quartz集中到webapp當(dāng)中還是有一定的風(fēng)險(xiǎn),同時(shí)同一個(gè)時(shí)間點(diǎn)執(zhí)行也不止一次。Robbin的解決辦法就是自己?jiǎn)为?dú)啟動(dòng)一個(gè)Job Server,來(lái)quartz跑job,不要部署在web容器中。
我也比較同意這個(gè)辦法。鑒于時(shí)間比較緊,就想有沒(méi)有比較方便的方法。其實(shí)把原來(lái)的webapp當(dāng)做一個(gè)quartz的容器就可以了。可以自己寫一個(gè)線程來(lái)跑應(yīng)用,再寫一個(gè)command啟動(dòng)這個(gè)線程就可以了。線程類很簡(jiǎn)單,如下:
public class StartServer { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( System.out.println("start server...."); while (true) { try { Thread.sleep(900); } catch (InterruptedException ex) { } } }; } |
去掉了系統(tǒng)的controller配置servlet.xml,運(yùn)行這個(gè)類就可以了。
在web-inf目錄下寫一個(gè)command來(lái)啟動(dòng)這個(gè)java類:
setlocal ENABLEDELAYEDEXPANSION if defined CLASSPATH (set CLASSPATH=%CLASSPATH%;.) else (set CLASSPATH=.) FOR /R .\lib %%G IN (*.jar) DO set CLASSPATH=!CLASSPATH!;%%G Echo The Classpath definition is==== %CLASSPATH% set CLASSPATH=./classes;%CLASSPATH% java com.company.job.StartServer |
這個(gè)command需要把需要的jar(web-inf/lib中)包都放到classpath中。
每次啟動(dòng)的時(shí)候執(zhí)行這個(gè)command就可以了。跟原來(lái)的應(yīng)用分開(kāi)了,調(diào)試起定時(shí)任務(wù)也不用影響到原來(lái)的應(yīng)用,還是比較方便的。部署的時(shí)候原樣拷貝一份,然后執(zhí)行這個(gè)command就好了,部署起來(lái)也比較方便。
接上一篇Hibernate 動(dòng)態(tài)HQL(http://www.aygfsteel.com/ghostzhang/archive/2011/09/08/358320.html ),開(kāi)發(fā)中經(jīng)常需要修改SQL或者HQL的語(yǔ)句,但是每次都要重啟服務(wù)器才能使之起作用,就想到在使用Spring配置多語(yǔ)言時(shí)有一個(gè)ReloadableResourceBundleMessageSource.java類,可以配置動(dòng)態(tài)加載多語(yǔ)言文件,為了配合動(dòng)態(tài)HQL并實(shí)現(xiàn)修改HQL語(yǔ)句不用重啟服務(wù)器,可以參考下這個(gè)類的實(shí)現(xiàn)。Java代碼如下:(ReloadableDynamicHibernate.java)
Spring 配置如下:
這樣就實(shí)現(xiàn)了每次修改SQL or HQL語(yǔ)句后不用重啟服務(wù)器,立刻看到結(jié)果,加快了開(kāi)發(fā)速度。
在開(kāi)發(fā)的時(shí)候,很多時(shí)候都遇到過(guò)需要?jiǎng)討B(tài)拼寫SQL,有的是在配置文件中寫SQL,有的是在Java代碼中拼寫SQL,以配置文件拼SQL的可以拿IBatis為代表,但是很多時(shí)候是使用Hibernate的,這個(gè)時(shí)候就想要是Hibernate能像IBatis那樣寫就好了。
這個(gè)時(shí)候就想到了模板語(yǔ)言和配置文件的結(jié)合。模板引擎可以選擇Velocity,簡(jiǎn)單而不失強(qiáng)大,配置文件可以模仿Hibernate的sql-query 的XML文件。
Sq-query的示例代碼如下(SQL or HQL):
在系統(tǒng)加載時(shí),需要把配置文件加載到系統(tǒng)中。加載代碼關(guān)鍵部分如下:
Spring的配置文件示例如下:
下一步是在使用時(shí)調(diào)用sql并調(diào)用模板方法,進(jìn)行sql動(dòng)態(tài)化。
還是DynamicHibernateImpl這個(gè)類
就這些。
大家也許有更好的方法,歡迎交流。
QQ:24889356