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
下面主要说一下我在升级过程中遇到的一些问题及解决办法。
l Maven的repository始终无法升级到SpringRC2,可能服务器有问题吧,目前暂时是从官方下载的整个SpringRC2的zip包。版本号是:3.1.0.RC2
l Hibernate可以从repository中升级到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升级到2.1.1,Ehcache-core升级到2.5.0
l Spring3.1取消了HibernateTemplate,因为Hibernate4的事务管理已经很好了,不用Spring再扩展了。所以以前的Dao需要改写,直接调用Hibernate 的Session进行持久化。
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>
<property name="hibernate.current_session_context_class">thread</property>
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输出是乱码的问题:原来使用的是:
<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>
这样比每个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;
}
现在改为:
@Lob
public byte[] getPic() {
return pic;
}
简单很多。
l 待续。。。
Shiro权限框架
开发系统中,少不了权限,目前java里的权限框架有SpringSecurity和Shiro(以前叫做jsecurity),对于SpringSecurity:功能太过强大以至于功能比较分散,使用起来也比较复杂,跟Spring结合的比较好。对于初学Spring Security者来说,曲线还是较大,需要深入学习其源码和框架,配置起来也需要费比较大的力气,扩展性也不是特别强。
对于新秀Shiro来说,好评还是比较多的,使用起来比较简单,功能也足够强大,扩展性也较好。听说连Spring的官方都不用Spring Security,用的是Shiro,足见Shiro的优秀。网上找到两篇介绍:http://www.infoq.com/cn/articles/apache-shiro http://www.ibm.com/developerworks/cn/opensource/os-cn-shiro/,官网http://shiro.apache.org/ ,使用和配置起来还是比较简单。下面只是简单介绍下我们是如何配置和使用Shiro的(暂时只用到了Shiro的一部分,没有配置shiro.ini文件)。
首先是添加过滤器,在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> |
权限的认证类:
public class ShiroDbRealm extends AuthorizingRealm { @Inject private UserService userService ;
/** * 认证回调函数,登录时调用. */ 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; } } /** * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用. */ 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(); }; } |
注册和获取当前登录用户:
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个:shiro-core.jar,shiro-spring.jar,shiro-web.jar。感觉shiro用起来比SpringSecurity简单很多。
在我们开发的一个系统中,有定时任务,自然就想到了Quartz,由于框架采用的Spring,Quartz跟Spring的集成也非常简单,所以就把Quartz配置到框架中,当系统启动后,定时任务也就自动启动。在开发的过程中一直没有发现问题,但是最后上线的时候,采用的是weblogic cluster,启动了4个节点,发现有的定时任务执行了不止一次,才恍然大悟,4个节点启动了4个应用,也就启动了4个定时任务,所以在同一个时间定时任务执行了不止一次。去网上搜索,发现Quartz也支持cluster,但是我觉得就我们的系统而言,没有必要采用cluster的定时任务,也许是比较懒吧,就想让定时任务只执行一次。在网上搜到了robbin的一篇文章(http://robbin.iteye.com/blog/40989 ),发现把quartz集中到webapp当中还是有一定的风险,同时同一个时间点执行也不止一次。Robbin的解决办法就是自己单独启动一个Job Server,来quartz跑job,不要部署在web容器中。
我也比较同意这个办法。鉴于时间比较紧,就想有没有比较方便的方法。其实把原来的webapp当做一个quartz的容器就可以了。可以自己写一个线程来跑应用,再写一个command启动这个线程就可以了。线程类很简单,如下:
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) { } } }; } |
去掉了系统的controller配置servlet.xml,运行这个类就可以了。
在web-inf目录下写一个command来启动这个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 |
这个command需要把需要的jar(web-inf/lib中)包都放到classpath中。
每次启动的时候执行这个command就可以了。跟原来的应用分开了,调试起定时任务也不用影响到原来的应用,还是比较方便的。部署的时候原样拷贝一份,然后执行这个command就好了,部署起来也比较方便。