??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
现在让我们看?span>Spring AOP是如何处理通知的?/span>
Spring的通知可以跨越多个被通知对象׃nQ或者每个被通知对象有自q通知。这分别对应 per-class?em>per-instance 通知?/span>
Per-class通知使用最为广泛。它适合于通用的通知Q如事务adisor。它们不依赖被代?的对象的状态,也不d新的状态。它们仅仅作用于Ҏ和方法的参数?/span>
Per-instance通知适合于导入,来支持入(mixinQ。在q种情况下,通知d状态到 被代理的对象?/span>
可以在同一?span>AOP代理中合用共享和per-instance通知?/span>
Spring提供几种现成的通知cdq可扩展提供L的通知cd。让我们看看基本概念和标准的通知cd?/span>
Spring中最基本的通知cd?em>interception around advice .
Spring使用Ҏ拦截器的around通知是和AOP联盟接口兼容的。实?span>around通知?c需要实现接?span>MethodInterceptorQ?/span>
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
invoke()Ҏ?em>MethodInvocation 参数暴露被调用的方法、目标连接点?span>AOP代理和传递给被调用方法的参数?invoke()Ҏ应该q回调用的结果:q接点的q回倹{?/span>
一个简单的MethodInterceptor实现看v来如?span>:
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
注意MethodInvocation?em>proceed()Ҏ的调用。这个调用会应用到目标连接点的拦截器链中的每一个拦截器。大部分拦截器会调用q个ҎQƈq回它的q回倹{但是, 一?span>MethodInterceptorQ和Maround通知一P可以q回不同的值或者抛Z个异常,?不调?span>proceedҎ。但是,没有好的原因你要q么做?/span>
MethodInterceptor提供了和其他AOP联盟的兼容实现的交互能力。这一节下?要讨论的其他的通知cd实现?span>AOP公共的概念,但是?span>Spring特定的方式。虽然用特?通知cd有很多优点,但如果你可能需要在其他?span>AOP框架中用,请坚持?span>MethodInterceptor around通知cd。注意目前切入点不能和其它框架交互操作,q且AOP联盟目前也没有定义切?Ҏ口?/span>
Before通知是一U简单的通知cd?q个通知不需要一?span>MethodInvocation对象Q因为它只在q入一个方法前被调用?/span>
Before通知的主要优Ҏ它不需要调?span>proceed() ҎQ?因此没有无意中忘掉l执行拦截器铄可能性?/span>
MethodBeforeAdvice接口如下所C?span> (Spring?span>API设计允许成员变量?span>before通知Q虽然一般的对象都可以应用成员变量拦截,?span>Spring 有可能永q不会实现它Q?/span>
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
注意q回cd?span>void?span> Before通知可以在连接点执行之前 插入自定义的行ؓQ但是不能改变返回倹{如果一?span>before通知抛出一个异常,q将中断拦截?铄q一步执行。这个异常将沿着拦截器链后退着向上传播。如果这个异常是unchecked的,或?出现在被调用的方法的{中,它将会被直接传递给客户代码Q否则,它将?span>AOP代理包装C?span>unchecked 的异帔R?/span>
下面?span>Spring中一?span>before通知的例子,q个例子计数所有正常返回的ҎQ?/span>
public class CountingBeforeAdvice implements MethodBeforeAdvice {
private int count;
public void before(Method m, Object[] args, Object target) throws Throwable {
++count;
}
public int getCount() {
return count;
}
}
Before通知可以被用于Q何类型的切入炏V?/span>
如果q接Ҏ出异常,Throws通知 在连接点q回后被调用?span>Spring提供强类型的throws通知。注意这意味着 org.springframework.aop.ThrowsAdvice接口不包含Q何方法: 它是一个标记接口,标识l定的对象实C一个或多个强类型的throws通知Ҏ。这些方法Ş?如下Q?/span>
afterThrowing([Method], [args], [target], subclassOfThrowable)
只有最后一个参数是必需的。这样从一个参数到四个参数Q依赖于通知是否Ҏ法和Ҏ 的参数感兴趣。下面是throws通知的例子?/span>
如果抛出RemoteException异常Q包括子c), q个通知会被调用
public class RemoteThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
}
如果抛出ServletException异常Q?下面的通知会被调用。和上面的通知不一P它声明了四个参数Q所以它可以讉K被调用的ҎQ方法的参数和目标对?span>:
public static class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something will all arguments
}
}
最后一个例子演CZ如何在一个类中用两个方法来同时处理 RemoteException?span>ServletException 异常。Q意个数的throwsҎ可以被组合在一个类中?/span>
public static class CombinedThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something will all arguments
}
}
Throws通知可被用于Mcd的切入点?/span>
Spring中的after returning通知必须实现 org.springframework.aop.AfterReturningAdvice 接口Q如下所C:
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable;
}
After returning通知可以讉Kq回|不能改变Q、被调用的方法、方法的参数和目标对象?/span>
下面?span>after returning通知l计所有成功的没有抛出异常的方法调用:
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
private int count;
public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
++count;
}
public int getCount() {
return count;
}
}
q方法不改变执行路径。如果它抛出一个异常,q个异常而不是返回值将被沿着拦截器链向上抛出?/span>
After returning通知可被用于Mcd的切入点?/span>
Spring?span>introduction通知看作一U特D类型的拦截通知?/span>
Introduction需要实?span>IntroductionAdvisor, ?span>IntroductionInterceptor接口Q?/span>
public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
l承?span>AOP联盟MethodInterceptor接口?span> invoke()Ҏ必须实现导入Q也是_如果被调用的Ҏ是在 导入的接口中Q导入拦截器负责处理q个Ҏ调用Q它不能调用proceed() Ҏ?/span>
Introduction通知不能被用于Q何切入点Q因为它只能作用于类层次上,而不是方法。你可以只用InterceptionIntroductionAdvisor来实现导入通知Q它有下面的ҎQ?/span>
public interface InterceptionIntroductionAdvisor extends InterceptionAdvisor {
ClassFilter getClassFilter();
IntroductionInterceptor getIntroductionInterceptor();
Class[] getInterfaces();
}
q里没有MethodMatcherQ因此也没有和导入通知兌?切入炏V只有类qo是合乎逻辑的?/span>
getInterfaces()Ҏq回advisor导入的接口?/span>
让我们看看一个来?span>Spring试套g中的单例子。我们假设想要导入下面的接口C?或者多个对象中:
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
q个例子演示了一?span>mixin。我们想要能?被通知对象cd转换?span>LockableQ不它们的cdQƈ且调?span>lock?span>unlockҎ。如果我们调?span> lock()ҎQ我们希望所?span>setterҎ抛出LockedException异常。这h们能d一个方面的对象不可变Q而它们不需要知道这一点:q是一个很好的AOP?子?/span>
首先Q我们需要一个做大量转化?span>IntroductionInterceptor?在这里,我们l承 org.springframework.aop.support.DelegatingIntroductionInterceptor 实用cR我们可以直接实?span>IntroductionInterceptor接口Q但是大多数情况?span> DelegatingIntroductionInterceptor是最合适的?/span>
DelegatingIntroductionInterceptor的设计是导?委托到真正实现导入接口的接口Q隐藏完成这些工作的拦截器。委托可以用构造方法参?讄CQ何对象中Q默认的委托是自己Q当无参数的构造方法被使用Ӟ。这样在下面的例子里Q委托是DelegatingIntroductionInterceptor的子c?span> LockMixin。给定一个委托(默认是自w)?span> DelegatingIntroductionInterceptor实例L被这个委托(而不 ?span>IntroductionInterceptorQ实现的所有接口,q支持它们中M一个导入。子cd LockMixin也可能调?span>suppressInterflace(Class intf) Ҏ隐藏不应暴露的接口。然而,不管IntroductionInterceptor 准备支持多少接口Q?span>IntroductionAdvisor控制哪个接口将被实?暴露。一个导入的接口隐藏目标的同一个接口的所有实现?/span>
q样Q?span>LockMixinl承DelegatingIntroductionInterceptor q自己实?span>Lockable。父c自动选择支持导入?span>LockableQ所以我们不需要指定它。用q种Ҏ我们可以导入L数量的接口?/span>
注意locked实例变量的用。这有效地添加额外的状态到目标 对象?/span>
public class LockMixin extends DelegatingIntroductionInterceptor
implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0)
throw new LockedException();
return super.invoke(invocation);
}
}
通常不要需要改?span>invoke()ҎQ实?span> DelegatingIntroductionInterceptorp够了Q如果是导入的方法, DelegatingIntroductionInterceptor实现会调用委托方法, 否则l箋沿着q接点处理。在现在的情况下Q我们需要添加一个检查:在上锁状态下不能调用setterҎ?/span>
所需的导?span>advisor是很单的。只有保存一个独立的 LockMixin实例Qƈ指定导入的接口,在这里就?span> Lockable。一个稍微复杂一点例子可能需要一个导入拦截器Q可?定义?span>prototypeQ的引用Q在q种情况下,LockMixin没有相关配置Q所以我们简单地 使用new来创建它?/span>
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
我们可以非常单地使用q个advisorQ它不需要Q何配|。(但是Q有一??/em>必要的:是不可能在没有IntroductionAdvisor 的情况下使用IntroductionInterceptor。) 和导入一P通常 advisor必须是针Ҏ个实例的Qƈ且是有状态的。我们会有不同的?span>LockMixinAdvisor 每个被通知对象Q会有不同的LockMixin?span> advisorl成了被通知对象的状态的一部分?/span>
和其?span>advisor一P我们可以使用 Advised.addAdvisor() Ҏ以编E地方式使用q种advisorQ或者在XML中配|(推荐q种方式Q?下面讨论所有代理创建,包括“自动代理创徏?span>”Q选择代理创徏以正地处理导入和有状态的混入?/span>
参考资料:
1Q?/font> http://www.javaresearch.org/article/showarticle.jsp?column=23&thread=41315
2Q?/font> http://tech.ccidnet.com/art/1112/20051114/371959_5.html
3Q?/font> http://www.7dspace.com/doc/21/0603/20063305365394884.htm
4Q?/font> http://barton131420.cnblogs.com/articles/280664.html
5Q?/font> http://www.opentown.info/bbs/viewtopic.php?t=7
log4j.rootLogger=DEBUG,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %5p (%F:%L) - %m%n |
Qbean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"Q?br>Qproperty name="dataSource"Q<ref bean="dataSource"/Q</propertyQ?br>Qproperty name="mappingResources"Q?br> QlistQ?br> QvalueQUser.hbm.xmlQ?valueQ?br> 错,改ؓQ? QvalueQcom/yz/spring/domain/User.hbm.xmlQ?valueQ?br> Q?listQ?br>Q?propertyQ?br>Qproperty name="hibernateProperties"Q?br>QpropsQ? Qprop key="hibernate.dialect"Q?net.sf.hibernate.dialect.MySQLDialect Q?propQ? Qprop key="hibernate.show_sql"QtrueQ?propQ? Q?propsQ? Q?propertyQ?br>Q?beanQ?/td> |
public class PostManageImpl extends BaseManage implements PostManage { private PostDAO dao = null; public void setPostDAO(PostDAO postDAO){ this.dao = postDAO; } } |
Qbean id="postManage" parent="txProxyTemplate"Q?br>Qproperty name="target"Q?br> Qbean class="com.yz.spring.service.implement.PostManageImpl"Q?br> Qproperty name="postDAO"Q<ref bean="postDAO"/Q</propertyQ??br> Qproperty name="dao"Q<ref bean="postDAO"/Q</propertyQ??br> Q?beanQ?br>Q?propertyQ?br>Q?beanQ?/td> |
Qbean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"Q?br>Qproperty name="transactionManager"Q<ref bean="transactionManager"/Q</propertyQ?br>Qproperty name="transactionAttributes"Q?br>QpropsQ?br>Qprop key="save*"QPROPAGATION_REQUIREDQ?propQ?br>Qprop key="remove*"QPROPAGATION_REQUIREDQ?propQ?br>Qprop key="*"QPROPAGATION_REQUIREDQ?propQ?br>Q?propsQ?br>Q?propertyQ?br>Q?beanQ?br> Qbean id="userManage" parent="txProxyTemplate"Q?br> Qproperty name="target"Q?br> Qbean class="com.yz.spring.service.implement.UserManageImpl"Q?br> Qproperty name="userDAO"Q<ref bean="userDAO"/Q</propertyQ?br> Q?beanQ?br> Q?propertyQ?br>Q?beanQ?/td> |
String[] paths = {"com/yz/spring/dao/hibernate/applicationContext-hibernate.xml", "com/yz/spring/service/applicationContext-service.xml"}; ctx = new ClassPathXmlApplicationContext(paths); |
QservletQ?br>Qservlet-nameQcontextQ?servlet-nameQ?br>Qservlet-classQorg.springframework.web.context.ContextLoaderServletQ?servlet-classQ?br>Qload-on-startupQ?Q?load-on-startupQ?br>Q?servletQ?br> Qcontext-paramQ?br>Qparam-nameQcontextConfigLocationQ?param-nameQ?br>Qparam-valueQ?WEB-INF/applicationContext-hibernate.xmlQ?param-valueQ?br>Qparam-valueQ?WEB-INF/applicationContext-service.xmlQ?param-valueQ?br>Q?context-paramQ?/td> |
Qcontext-paramQ?br>Qparam-nameQlog4jConfigLocationQ?param-nameQ?br>Qparam-valueQ?WEB-INF/log4j.propertiesQ?param-valueQ?br>Q?context-paramQ?/td> |
Expert One on one J2EE Design and Development Expert One on one J2EE Development Without EJB |
http://www.martinfowler.com/articles/injection.html |
http://www.jactiongroup.net/reference/html/index.htmlQ中文版Q未全部译Q?/td> |
它有两种配置方式OpenSessionInViewInterceptor ?span style="FONT-SIZE: 10pt; COLOR: #4b4b4b; FONT-FAMILY: Verdana">OpenSessionInViewFilter(具体参看SpringSide)
Q功能相同,只是一个在 web.xml 配置Q另一个在 application.xml 配置而已?/span>Open Session In View ?/span> request ?/span> session l定到当?/span> thread 期间一直保?/span> hibernate session ?/span> open 状态,?/span> session ?/span> request 的整个期间都可以使用Q如?/span> View 层里 PO 也可?/span> lazy loading 数据Q如 ${ company.employees } 。当 View 层逻辑完成后,才会通过 Filter ?/span> doFilter Ҏ?/span> Interceptor ?/span> postHandle Ҏ自动关闭 session ?/span>
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则等于没?span class="me1">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>
很多人在使用OpenSessionInViewq程中提及一个错误:
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。然后把该sessionFactoryl定到TransactionSynchronizationManagerQrequest的整个过E都使用同一个sessionQ在hq后再接除该sessionFactory的绑定,最?span class="me1">closeSessionIfNecessaryҎ该session是否已和transactionl定来决定是否关闭session。在q个q程中,若HibernateTemplate 发现自当前session有不是readOnly的transactionQ就会获取到FlushMode.AUTO SessionQҎ拥有写权限?/p>
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转ؓFlush.AUTO,拥有insert,update,delete操作权限Q如果没有transactionQƈ且没有另外h为地设flush model的话Q则doFilter的整个过E都是Flush.NEVER。所以受transaction保护的方法有写权限,没受保护的则没有?/p>
采用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>
对于上例Q则以save,add,update,remove开头的Ҏ拥有可写的事务,如果当前有某个方法,如命名ؓimportExcel()Q则因没有transaction而没有写权限Q这时若Ҏ内有insert,update,delete操作的话Q则需要手动设|flush model为Flush.AUTO,?/p>
session.setFlushMode(FlushMode.AUTO); session.save(user); session.flush();
管Open Session In View看v来还不错Q其实副作用不少。看回上面OpenSessionInViewFilter的doFilterInternalҎ代码Q这个方法实际上是被父类的doFilter调用的,因此Q我们可以大U了解的OpenSessionInViewFilter调用程: request(h)->open sessionq开始transaction->controller->View(Jsp)->l束transactionqclose session.
一切看h很正,其是在本地开发测试的时候没出现问题Q但试想下如果流E中的某一步被d的话Q那在这期间connection׃直被占用而不释放。最有可能被d的就是在写Jspq步Q一斚w可能是页面内容大Qresponse.write的时间长Q另一斚w可能是网速慢Q服务器与用户间传输旉久。当大量q样的情况出现时Q就有连接池q接不Q造成面假死现象?/p>
Open Session In View是个双刃剑,攑֜公网上内容多量大的|站h用?/p>
q里只分n自己的唯一l验? spring + quartz.
其实spring对quartz装的完无?哈哈
试牛刀开始了...
java代码呢就是区区几?
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.apache.log4j.*;
public class ContentArbitrateServiceJob extends QuartzJobBean {
public static Logger log = Logger.getLogger(ContentArbitrateServiceJob.class);
private int timeout;
public void setTimeout(int timeout){
this.timeout = timeout;
}
protected void executeInternal(JobExecutionContext arg0)
throws JobExecutionException {
// TODO Auto-generated method stub
try{
log.info("pȝ监督仲裁处理d开?gt;........");
//业务逻辑代码调用
log.info("pȝ监督仲裁处理dl束!");
}catch(Exception e){
log.error("pȝ监督仲裁处理d出现异常",e);
}
}
}
下面是看配|文仉面的道道?其实也不?
<bean name="contentarbitrateservicejob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>com.xuedu.ContentArbitrateServiceJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="timeout">
<value>5</value>
</entry>
</map>
</property>
</bean>
<!-- 配置触发?-->
<bean id="cronTriggerCA" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="contentarbitrateservicejob"/>
</property>
<!-- 每天?点到21??0分钟触发Q具体说明见附录 -->
<property name="cronExpression">
<value>0 50 08-21 * * ?</value>
</property>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!-- d触发?-->
<property name="triggers">
<list>
<ref local="cronTriggerCA"/>
</list>
</property>
</bean>
也很单吧.
至于cronExpresession的设定格式呢,弟附下:
字段 允许?允许的特D字W?
U?0-59 , - * /
?0-59 , - * /
时 0-23 , - * /
日期 1-31 , - * ? / L W C
月䆾 1-12 或?JAN-DEC , - * /
星期 1-7 或?SUN-SAT , - * ? / L C #
q_可选) 留空, 1970-2099 , - * /
表达?意义
"0 0 12 * * ?" 每天中午12点触?
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005q的每天上午10:15触发
"0 * 14 * * ?" 在每天下?点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下?点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下?点到2:55期间和下?点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下?点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下?:10?:44触发
"0 15 10 ? * MON-FRI" 周一臛_五的上午10:15触发
"0 15 10 15 * ?" 每月15日上?0:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002q至2005q的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上?0:15触发
至于每个W号 看看例子好?很简单了.
Acegi 的配|看h非常复杂,但事实上在实际项目的安全应用中我们ƈ不需要那么多功能,清楚的了解Acegi配置中各的功能Q有助于我们灉|的运用Acegi于实践中。?/font>
Acegi 的配|看h非常复杂,但事实上在实际项目的安全应用中我们ƈ不需要那么多功能,清楚的了解Acegi配置中各的功能Q有助于我们灉|的运用Acegi于实践中?/font>
2.1 在Web.xml中的配置
1) FilterToBeanProxy
Acegi通过实现了Filter接口的FilterToBeanProxy提供一U特D的使用Servlet Filter的方式,它委托Spring中的Bean -- FilterChainProxy来完成过滤功能,q好处是化了web.xml的配|,q且充分利用了Spring IOC的优ѝFilterChainProxy包含了处理认证过E的filter列表Q每个filter都有各自的功能?/font>
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
2) filter-mapping
<filter-mapping>限定了FilterToBeanProxy的URL匚w模式,只有*.do?.jsp?j_acegi_security_check 的请求才会受到权限控Ӟ对javascript,css{不限制?/font>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/j_acegi_security_check</url-pattern>
</filter-mapping>
3) HttpSessionEventPublisher
<listener>的HttpSessionEventPublisher用于发布HttpSessionApplicationEvents和HttpSessionDestroyedEvent事glspring的applicationcontext?/font>
<listener>
<listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
</listener>
FilterChainProxy会按序来调用这些filter,使这些filter能n用Spring ioc的功? CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义了url比较前先转ؓ写Q PATTERN_TYPE_APACHE_ANT定义了用Apache ant的匹配模式?/font>
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,
basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,
exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
1) authenticationManager
起到认证理的作用,它将验证的功能委托给多个ProviderQƈ通过遍历Providers, 以保证获取不同来源的w䆾认证Q若某个Provider能成功确认当前用Lw䆾Qauthenticate()Ҏ会返回一个完整的包含用户授权信息的Authentication对象Q否则会抛出一个AuthenticationException?br />Acegi提供了不同的AuthenticationProvider的实?如:
DaoAuthenticationProvider 从数据库中读取用户信息验证n?br /> AnonymousAuthenticationProvider 匿名用户w䆾认证
RememberMeAuthenticationProvider 已存cookie中的用户信息w䆾认证
AuthByAdapterProvider 使用容器的适配器验证n?br /> CasAuthenticationProvider ҎYale中心认证服务验证w䆾, 用于实现单点登陆
JaasAuthenticationProvider 从JASS登陆配置中获取用户信息验证n?br /> RemoteAuthenticationProvider Ҏq程服务验证用户w䆾
RunAsImplAuthenticationProvider 对n份已被管理器替换的用戯行验?br /> X509AuthenticationProvider 从X509认证中获取用户信息验证n?br /> TestingAuthenticationProvider 单元试时?/font>
每个认证者会对自己指定的证明信息q行认证Q如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationTokenq个证明信息q行认证?/font>
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
<ref local="rememberMeAuthenticationProvider"/>
</list>
</property>
</bean>
2) daoAuthenticationProvider
q行单的Z数据库的w䆾验证。DaoAuthenticationProvider获取数据库中的̎号密码ƈq行匚wQ若成功则在通过用户w䆾的同时返回一个包含授权信息的Authentication对象Q否则n份验证失败,抛出一个AuthenticatiionException?/font>
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="jdbcDaoImpl"/>
<property name="userCache" ref="userCache"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>
3) passwordEncoder
使用加密器对用户输入的明文进行加密。Acegi提供了三U加密器:
PlaintextPasswordEncoder—默认,不加密,q回明文.
ShaPasswordEncoder—哈希算?SHA)加密
Md5PasswordEncoder—消息摘?MD5)加密
<bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>
4) jdbcDaoImpl
用于在数据中获取用户信息。 acegi提供了用户及授权的表l构Q但是您也可以自己来实现。通过usersByUsernameQueryq个SQL得到你的(用户ID,密码,状态信?;通过authoritiesByUsernameQueryq个SQL得到你的(用户ID,授权信息)
<bean id="jdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="usersByUsernameQuery">
<value>select loginid,passwd,1 from users where loginid = ?</value>
</property>
<property name="authoritiesByUsernameQuery">
<value>select u.loginid,p.name from users u,roles r,permissions p,user_role ur,role_permis rp where u.id=ur.user_id and r.id=ur.role_id and p.id=rp.permis_id and
r.id=rp.role_id and p.status='1' and u.loginid=?</value>
</property>
</bean>
5) userCache & resourceCache
~存用户和资源相对应的权限信息。每当请求一个受保护资源ӞdaoAuthenticationProvider׃被调用以获取用户授权信息。如果每ơ都从数据库获取的话Q那代h很高Q对于不常改变的用户和资源信息来_最好是把相x权信息缓存v来?详见
2.6.3 资源权限定义扩展
)
userCache提供了两U实? NullUserCache和EhCacheBasedUserCache, NullUserCache实际上就是不q行M~存QEhCacheBasedUserCache是用Ehcache来实现缓功能?/font>
<bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="cacheName" value="userCache"/>
</bean>
<bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache" autowire="byName">
<property name="cache" ref="userCacheBackend"/> </bean>
<bean id="resourceCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager" ref="cacheManager"/> <property name="cacheName" value="resourceCache"/>
</bean>
<bean id="resourceCache" class="org.springside.modules.security.service.acegi.cache.ResourceCache" autowire="byName">
<property name="cache" ref="resourceCacheBackend"/>
</bean>
6) basicProcessingFilter
用于处理HTTP头的认证信息Q如从Springq程协议(如Hessian和Burlap)或普通的览器如IE,Navigator的HTTP头中获取用户信息Q将他们转交l通过authenticationManager属性装配的认证理器。如果认证成功,会将一个Authentication对象攑ֈ会话中,否则Q如果认证失败,会将控制转交l认证入口点(通过authenticationEntryPoint属性装?
<bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint"/>
</bean>
7) basicProcessingFilterEntryPoint
通过向浏览器发送一个HTTP401(未授?消息Q提C用L录?br />处理ZHTTP的授权过E, 在当验证q程出现异常后的"d"Q通常实现转向、在response里加入error信息{功能?/font>
<bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint"> <property name="realmName" value="SpringSide Realm"/>
</bean>
8) authenticationProcessingFilterEntryPoint
当抛出AccessDeniedExceptionӞ用户重定向到登录界面。属性loginFormUrl配置了一个登录表单的URL,当需要用L录时QauthenticationProcessingFilterEntryPoint会将用户重定向到该URL
<bean id="authenticationProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl">
<value>/security/login.jsp</value>
</property>
<property name="forceHttps" value="false"/>
</bean>
1) httpSessionContextIntegrationFilter
每次request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象Q在request完后, 又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi filter前用,使之能跨多个请求?/font>
<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"></bean>
<bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
2) httpRequestAccessDecisionManager
l过投票机制来决定是否可以访问某一资源(URL或方?。allowIfAllAbstainDecisions为false时如果有一个或以上的decisionVoters投票通过,则授权通过。可选的决策机制有ConsensusBased和UnanimousBased
<bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
3) roleVoter
必须是以rolePrefix讑֮的value开头的权限才能q行投票,如AUTH_ , ROLE_
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
<property name="rolePrefix" value="AUTH_"/>
</bean>
4Q?strong>exceptionTranslationFilter
异常转换qo器,主要是处理AccessDeniedException和AuthenticationExceptionQ将l每个异常找到合适的"d"
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="authenticationProcessingFilterEntryPoint"/>
</bean>
5) authenticationProcessingFilter
和servlet spec差不?处理登陆h.当n份验证成功时QAuthenticationProcessingFilter会在会话中放|一个Authentication对象Qƈ且重定向到登录成功页?br /> authenticationFailureUrl定义登陆p|时{向的面
defaultTargetUrl定义登陆成功时{向的面
filterProcessesUrl定义登陆h的页?br /> rememberMeServices用于在验证成功后dcookie信息
<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl">
<value>/security/login.jsp?login_error=1</value>
</property>
<property name="defaultTargetUrl">
<value>/admin/index.jsp</value>
</property>
<property name="filterProcessesUrl">
<value>/j_acegi_security_check</value>
</property>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
6) filterInvocationInterceptor
在执行{向url前检查objectDefinitionSource中设定的用户权限信息。首先,objectDefinitionSource中定义了讉KURL需要的属性信?q里的属性信息仅仅是标志Q告诉accessDecisionManager要用哪些voter来投?。然后,authenticationManager掉用自己的provider来对用户的认证信息进行校验。最后,有投者根据用h有认证和讉Kurl需要的属性,调用自己的voter来投,军_是否允许讉K?/font>
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource" ref="filterDefinitionSource"/>
</bean>
7) filterDefinitionSource (详见
2.6.3 资源权限定义扩展
)
自定义DBFilterInvocationDefinitionSource从数据库和cache中读取保护资源及光要的讉K权限信息
<bean id="filterDefinitionSource" class="org.springside.modules.security.service.acegi.DBFilterInvocationDefinitionSource">
<property name="convertUrlToLowercaseBeforeComparison" value="true"/>
<property name="useAntPath" value="true"/>
<property name="acegiCacheManager" ref="acegiCacheManager"/>
</bean>
(详见 2.6.3 资源权限定义扩展 )
1) methodSecurityInterceptor
在执行方法前q行拦截Q检查用h限信?br />2) methodDefinitionSource
自定义MethodDefinitionSource从cache中读取权?/font>
<bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource" ref="methodDefinitionSource"/>
</bean>
<bean id="methodDefinitionSource" class="org.springside.modules.security.service.acegi.DBMethodDefinitionSource">
<property name="acegiCacheManager" ref="acegiCacheManager"/>
</bean>
采用
http://jcaptcha.sourceforge.net
作ؓ通用的验证码ҎQ请参考SpringSide中的例子Q或|上的:
http://www.coachthrasher.com/page/blog?entry=jcaptcha_with_appfuse
?/font>
差沙在此q程中又发现acegi logout filter的错误,q行了修正?/font>
One of the sweetiest things the Spring framework gives is Hibernate support is built-in by the time it's born.
Following is a quick guide to configure Hibernate's SessionFactory in Spring. For a detailed version about Spring's Hibernate support, read http://www.springframework.org/docs/data_access.html .
With Spring, Hibernate's SessionFactory no longer needs to bind itself to JNDI; nor use Hibernate's own hibernate.cfg.xml method, which is a little bit tricky to code in Hibernate 2.x (as Hibernate2 doesn't use the once-and-only-once configure() anymore).
Instead, we use org.springframework.orm.hibernate.LocalSessionFactoryBean .
<bean id= "MySessionFactory" class = "org.springframework.orm.hibernate.LocalSessionFactoryBean" >
<property name= "mappingResources" >
<list>
<value>mappings/Book.hbm.xml</value>
<value>mappings/Patron.hbm.xml</value>
<value>mappings/BorrowRecord.hbm.xml</value>
</list>
</property>
<property name= "hibernateProperties" >
<props>
<prop key= "hibernate.dialect" >net.sf.hibernate.dialect.MySQLDialect</prop>
<prop key= "hibernate.query.substitutions" > true = 1 false = 0 </prop>
<prop key= "hibernate.show_sql" > false </prop>
<prop key= "hibernate.use_outer_join" > false </prop>
</props>
</property>
<property name= "dataSource" ><ref bean= "MyDataSource" /></property>
</bean>
The above parameters are simple and verbose:
However, we don't need to configure a transaction manager inside the SessionFactory, as we will see below.
After configuring this, we need to provide a setter method in our business objects that need to use Hibernate's SessionFactory:
import net.sf.hibernate.SessionFactory;
....
public class MyBusinessObjectImpl implements MyBusinessObject
{
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory)
{
this .sessionFactory = sessionFactory;
}
public SessionFactory getSessionFactory()
{
return this .sessionFactory;
}
....
and hook it up to Spring.
<bean id= "MyBusinessObject" class = "library.MyBusinessObjectImpl" >
<property name= "sessionFactory" >
<ref bean= "MySessionFactory" />
</property>
</bean>
How about transactions support? And how do I get Hibernate's Session inside my business objects?
That's Spring framework's another power - HibernateInterceptor and TransactionInterceptor. With them, together with configurations made in Spring, methods inside business objects don't need to write a single line of code for that; instead, a Session will be bound to the business object's current thread, opened and closed automatically; and a transaction will also begin and end automatically.
The sequence is like this:
This is done with the help of Spring's AOP capability.
However, we need to configure a transaction manager first.
<bean id= "MyTransactionManager" class = "org.springframework.transaction.jta.JtaTransactionManager" />
The above configures a transaction manager that will access an UserTransaction inside the environment, usually in a J2EE container, or a servlet container with transaction support.
Alternately you may want to have a look at org.springframework.orm.hibernate.HibernateTransactionManager .
Next, we need to define the transaction attribute for our business methods. This is done in org.springframework.transaction.interceptor.TransactionInterceptor ? .
<bean id= "MyTransactionInterceptor"
class = "org.springframework.transaction.interceptor.TransactionInterceptor" >
<property name= "transactionManager" ><ref bean= "MyTransactionManager" /></property>
<property name= "transactionAttributeSource" >
<value>
library.MyBusinessImpl.borrowBook=PROPAGATION_REQUIRED
library.MyBusinessImpl.returnBook=PROPAGATION_REQUIRED
library.BookSearchImpl.*=PROPAGATION_SUPPORTS
</value>
</property>
</bean>
You can use a wildcard to tell every method in that business object uses the same transaction attribute. However it is not recommended, as the private methods used inside the business object will have transactions too, which you may not need to.
By the way, the TransactionInterceptor's default behaviour is to commit an transaction anyway, and rollback whenever a RuntimeException is caught - much the same as EJB's behaviour. If you want to control TransactionInterceptor's behaviour when exception is caught, you may tell it with a plus (+) or minus (-) sign, followed by the name of the exception, to commit or rollback a transaction even if an exception mentioned is caught.
An example:
library.MyBusinessImpl.addBook=PROPAGATION_REQUIRED,-SeriesNotFoundException,+CategoryNotFoundException
This means that when SeriesNotFoundException is thrown inside the addBook() method, the transaction will roll back; on the other hand when CategoryNotFoundException is thrown the transaction will be commited anyway.
In most cases you don't need to specify the exception's name with the package it belongs; you just need to specify simply the exception's name.
Then our business objects will need to define as an AOP "target". This is just a change in a name; make sure your code will not call these business objects directly. So change the above business object declaration to
<bean id= "MyBusinessObjectTarget" class = "library.MyBusinessObjectImpl" >
<property name= "sessionFactory" >
<ref bean= "MySessionFactory" />
</property>
</bean>
Final step is to expose the business object on Spring's ApplicationContext, but not the business object itself; instead we use a ProxyFactoryBean, provided by Spring.
<bean id= "MyBusinessObject" class = "org.springframework.aop.framework.ProxyFactoryBean" >
<property name= "proxyInterfaces" >
<value>library.MyBusinessObject</value>
</property>
<property name= "interceptorNames" >
<list>
<value>MyTransactionInterceptor</value>
<value>MyHibernateInterceptor</value>
<value>MyBusinessObjectTarget</value>
</list>
</property>
</bean>
Then in our business method we just need to obtain the Session instance, and ignore everything else - everything is done behind the scene.
import net.sf.hibernate.*;
import org.springframework.orm.hibernate.SessionFactoryUtils;
....
public Book findBook( int bookID) throws BookNotFoundException, DataAccessException
{
//get the Session instance already bound to current thread and opened
Session session = SessionFactoryUtils.getSession(getSessionFactory(), false );
try
{
Book book = (Book)session.load(Book.class, bookID);
return book;
}
catch (ObjectNotFoundException e)
{
throw new BookNotFoundException();
}
catch (HibernateException e)
{
throw SessionFactoryUtils.convertHibernateAccessException(e);
}
}
Alternately you may also use HibernateTemplate and TransactionTemplate, though the above method is simpler. Read http://www.hibernate.org/110.html for details (this page seems more updated than the one in Spring?).
1 Message source 的声明,重要用于pȝ的信息提C?/span>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"><value>messages</value></property>
</bean>
2 属性值的声明Q主要ؓ Bean 声明文g中用:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>WEB-INF/mail.properties</value>
<value>WEB-INF/jdbc.properties</value>
</list>
</property>
</bean>
3 Custom Editor 的注册,以下是日期的注册Q?/span>
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date">
<bean class="org.springframework.beans.propertyeditors.CustomDateEditor">
<constructor-arg index="0">
<bean class="java.text.SimpleDateFormat">
<constructor-arg><value>yyyy-MM-dd</value></constructor-arg>
</bean>
</constructor-arg>
<constructor-arg index="1"><value>false</value></constructor-arg>
</bean>
</entry>
</map>
</property>
</bean>
4 数据库连接池的设|:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
5 hibernate 的设|:
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="mappingResources">
<value>mapping.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
</props>
</property>
</bean>
6 Jotm 的事务设|:
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransaction"><ref local="jotm"/></property>
</bean>
7 Hibernate 的事务设|:
<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
8 Bean 的事务声明:
<bean id="clinic" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref local="hibernateTransactionManager"/></property>
<property name="target"><ref local="clinicTarget"/></property>
<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="store*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
9 Email 的发送者声明:
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host"><value>${mail.host}</value></property>
</bean>
10 基本 Url Mapping 的设|:
<bean id="DemoController" class="cn.edu.bit82.DemoController"/>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello.html">DemoController</prop>
<prop key="*">SecondController</prop>
</props>
</property>
</bean>
ȝQ?/span> 以上是一些常用的 Bean 的声明,你一般会用到的,你可以?/span> IntelliJ ?/span> Live Template 功能Q可以设|某些参敎ͼ很快完成了 Bean 的声明?/span>
/*
* Created on 2004-8-25 by simba.
*
*/
package com.simba.blog.util;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.opensymphony.webwork.ServletActionContext;
import com.simba.blog.service.BlogService;
/**
* @author simba
*
* email: simbasun@msn.com
*/
public class ServiceLocator
{
//the catalog service bean name
private static final String BLOG_SERVICE_BEAN_NAME = "blogService";
//the user service bean name
private static final String USER_SERVICE_BEAN_NAME = "userService";
//the logger for this class
private Log logger = LogFactory.getLog(this.getClass());
//the Spring application context
private ApplicationContext appContext;
//the cached catalog service
private BlogService blogService;
//the cached user service
//private UserService userService;
/**
* Constructor.
* <p>
* The following steps being done:
* <ul>
* <li>retrieve Spring application context from servlet context.
* <li>look up <code>CatalogService</code> from Spring application
* context.
* <li>look up <code>UserService</code> from Spring applicatino context.
* </ul>
*/
public ServiceLocator()
{
/*InputStream is = getClass().getResourceAsStream("springapp-servlet.xml");
XmlBeanFactory bf = new XmlBeanFactory(is);
blogService = (BlogService) bf.getBean("blogService");*/
ServletContext context = ServletActionContext.getServletContext();
this.appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
this.blogService = (BlogService)this.lookupService(BLOG_SERVICE_BEAN_NAME);
/*
* this.userService = (UserService)this.lookupService(USER_SERVICE_BEAN_NAME);
*/
this.logger.info("Service locator bean is initialized");
}
/**
* Lookup service based on service bean name.
*
* @param serviceBeanName the service bean name
* @return the service bean
*/
public Object lookupService(String serviceBeanName)
{
return appContext.getBean(serviceBeanName);
}
/**
* @return Returns the blogService.
*/
public BlogService getBlogService()
{
return blogService;
}
}
[in web.xml]
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- OR USE THE CONTEXTLOADERSERVLET INSTEAD OF THE LISTENER
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
-->
By: icess blog: http://blog.matrix.org.cn/page/icess
q次来看看用Spring的Hibernate模板来操作数? Spring提供了Hibernate的一层包?使Hibernate使用h更加方便,其是结合Hibernate Annotation? 配置文g更少,l护更加? 下面来看看吧.
下面是一个测试数据实体类TestData.java 和前一提到的实体cM?只不q用了Annotation注释.
package
test.orm.hibernate.model;
import
javax.persistence.Basic;
import
javax.persistence.Entity;
import
javax.persistence.Id;
import
javax.persistence.Table;
@Entity
@Table(name =
"test"
,schema =
"APP"
)
public class
TestData {
private int
id;
private
String name;
public
TestData(
int
id, String name) {
this
.id = id;
this
.name = name;
}
public
TestData() {}
@Id
public int
getId() {
return
id;
}
public void
setId(
int
id) {
this
.id = id;
}
@Basic
public
String getName() {
return
name;
}
public void
setName(String name) {
this
.name = name;
}
}
下面是测试Hibernate模板的类, TestHibernateTemplate.java
package
test.orm.hibernate;
import
java.sql.SQLException;
import
org.hibernate.HibernateException;
import
org.hibernate.Session;
import
org.springframework.orm.hibernate3.HibernateCallback;
import
org.springframework.orm.hibernate3.HibernateTemplate;
import
test.orm.hibernate.model.TestData;
public class
TestHibernateTemplate {
private
HibernateTemplate hibernateTemplate;
public
TestHibernateTemplate() {}
public
HibernateTemplate getHibernateTemplate() {
return
hibernateTemplate;
}
public void
setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this
.hibernateTemplate = hibernateTemplate;
}
//试使用HibernateTemplate来操作数?/font>
public
TestData getTestData(
final int
id) {
return
(TestData) hibernateTemplate.execute(
new
HibernateCallback() {
public
Object doInHibernate(Session s)
throws
HibernateException, SQLException {
// TODO Auto-generated method stub
return
s.get(TestData.class, id);
}
});
}
// 上面查询数据的方法 用了HibernateCallBack接口,对于q样单的查询,可以使用下面由HibernateTemplate 提供的更单的Ҏ
public
TestData getTestData2(
final int
id) {
return
(TestData) hibernateTemplate.get(TestData.class, id);
}
/* 如果上面的两处方法用Load Ҏ的时, 抛出延迟加蝲属性异? Spring理Session的问? 应该是Spring用完Session 然后关闭了,
* 所以不可以延迟加蝲, 也就不可以用load Ҏ?,????? Z????*/
// 其他操作数据Ҏ ? 插入,修改.... 和用Hibernate session 差不? HibernateTemplate只是Session的包?/font>
}
可以看到使用Hibernate模板来操作数?是多么简?
注意: 上面注释?提到了在使用LoadҎ时?会有问题, Session in view 时候用load是不会出问题? q里Spring提供的方法应该是按照Hibernate的语义写的吧.
下面是用来试上面的类是否正常工作的测试类?TestApp.java
package
test.orm.hibernate;
import
org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import
test.jdbc.DatabaseUtils;
import
test.orm.hibernate.model.TestData;
public class
TestApp {
/**
*
@param
args
*/
public static void
main(String[] args) {
// TODO Auto-generated method stub
DatabaseUtils dataUtils =
new
DatabaseUtils();
dataUtils.connect();
System.out.println(
"Open database:!"
);
ApplicationContext context =
new
ClassPathXmlApplicationContext(
"test/orm/hibernate/spring-hibernate.xml"
);
TestHibernateTemplate hibernateTemplate = (TestHibernateTemplate) context.getBean(
"testDao"
);
System.out.println(
"name : "
);
TestData data = hibernateTemplate.getTestData(
9
);
System.out.println(
"name : "
+ data.getName());
data = hibernateTemplate.getTestData2(
3
);
System.out.println(
"name 2: "
+ data.getName());
dataUtils.disconnect();
}
}
注意: 本测试中用到了上一用到?font color="#000000">DatabaseUtils.java 工具cL操作Derby数据?
?/span>
以上实例可以看出,在Spring中用Hibernate,效率更高一? 你不q样认ؓ?
同时
Spring也提供了 JDO{其他一些数据操作框架的包装,使用上基本上 是一Lq里׃在详qC.
下一ơ我们来看看Spring的I18N处理,和自定义属性编辑器的实?
BY: icess Blog: http://blog.matrix.org.cn/page/icess
在Spring中处理I18N问题和用Java里面的类基本上是一L.使用org.springframework.context.support.ResourceBundleMessageSource
然后注入资源文g(一个名字ؓbasename的属?,然后可以在Context中用资源文件了, 如下Z个配|示? test.xml
<?
xml version = "1.0" encoding = "UTF-8" ?><!
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "spring-beans.dtd" ><
beans >< bean id = "messageSource" class = "org.springframework.context.support.ResourceBundleMessageSource" >
< property name = "basename" >
<!-- 注意此处讄 资源 名字 和\?-->
< value > test/i18n/test </ value >
</ property >
</ bean >
</
beans >下面源文?test.properties
name =
\u51B0\u96E8
sex =
\u5148\u751Ftest_zh.properties
name =
\u51B0\u96E8
sex =
\u5148\u751Ftest_en_US.properties
name =
ice rain
sex =
male下面是一个简单的试c?
package
test.i18n;import
java.util.Locale;import
org.springframework.context.ApplicationContext;import
org.springframework.context.support.ClassPathXmlApplicationContext;public
class TestI18n { /*** @param args
*/
public static void main(String[] args) { // TODO Auto-generated method stub
ApplicationContext context =
new ClassPathXmlApplicationContext( "test/i18n/test.xml" );String text = context.getMessage(
"sex" , new Object[0], Locale. US );String textZH = context.getMessage(
"sex" , new Object[0], Locale. CHINA );System.
out .println(text + " 中文:" +textZH);}
}
很简?q样可以了.
下面来看看Spring中的属性自定义~辑?q个和Hibernate中的自定义属性差不多 ? 例如下面我们要看C例子,映射一个电话号??font size="2">areaCode,prefix?number, 如果不用自定义属性编辑器那么p分别注入上面?个代?ȝ. 如果使用自定义属性编辑器,直接注入一?分开的数字序列就可以??/p>
888-666-9999
.在下面的例子中的Contact.javacL个PhoneNumber属?里面保存了上面的3个代?两个cȝ代码如下:
package
test.propertyEditor;
public class
Contact {
private
PhoneNumber phoneNumber;
private
String name;
public
Contact() {}
public
String getName() {
return
name;
}
public void
setName(String name) {
this
.name = name;
}
public
PhoneNumber getPhoneNumber() {
return
phoneNumber;
}
public void
setPhoneNumber(PhoneNumber phoneNumber) {
this
.phoneNumber = phoneNumber;
}
}
PhoneNumber.java
package
test.propertyEditor;
public class
PhoneNumber {
private
String areaCode;
private
String prefix;
private
String number;
public
PhoneNumber() {
}
public
PhoneNumber(String areaCode,String prefix,String number) {
this
.areaCode = areaCode;
this
.prefix = prefix;
this
.number = number;
}
public
String getAreaCode() {
return
areaCode;
}
public void
setAreaCode(String areaCode) {
this
.areaCode = areaCode;
}
public
String getNumber() {
return
number;
}
public void
setNumber(String number) {
this
.number = number;
}
public
String getPrefix() {
return
prefix;
}
public void
setPrefix(String prefix) {
this
.prefix = prefix;
}
}
然后定义一个用来编辑PhoneNumber的编辑器PhoneEditor.java 如下:
package
test.propertyEditor;
import
java.beans.PropertyEditorSupport;
public class
PhoneEditor
extends
PropertyEditorSupport {
public void
setAsText(String textValue) {
String stripped = stripNonNumber(textValue);
String areaCode = stripped.substring(
0
,
3
);
String prefix = stripped.substring(
3
,
6
);
String number = stripped.substring(
6
);
PhoneNumber phone =
new
PhoneNumber(areaCode,prefix,number);
setValue(phone);
}
private
String stripNonNumber(String original) {
StringBuilder allNumeric =
new
StringBuilder();
for
(
int
i =
0
; i < original.length(); i ++) {
char
c = original.charAt(i);
if
(Character.isDigit(c)) {
allNumeric.append(c);
}
}
return
allNumeric.toString();
}
}
l承java里面的属性编辑器,实现里面的一个方法就可以? 下面是在配|文件中注册该编辑器.如下:
testPropertyEditor.xml
<?
xml version = "1.0" encoding = "UTF-8" ?><!
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "spring-beans.dtd" ><
beans >< bean id = "customEditorConfigurer" class = "org.springframework.beans.factory.config.CustomEditorConfigurer" >
< property name = "customEditors" >
< map >
< entry key = "test.propertyEditor.PhoneNumber" >
< bean id = "phoneEditor" class = "test.propertyEditor.PhoneEditor" ></ bean >
</ entry >
</ map >
</ property >
</ bean >
<!-- 如果不注册上面自定义Editor的实? 需要注册一个PhoneNumber的bean,讄其属性然后再注册
Contact的PhoneNumber的属?/p>
-->
< bean id = "contact" class = "test.propertyEditor.Contact" >
< property name = "phoneNumber" >
< value > 888-666-9999 </ value >
</ property >
</ bean >
</
beans >最后来试一下注册的l果是否正确:
package
test.propertyEditor;
import
org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public class
TestPropertyEditor {
/**
*
@param
args
*/
public static void
main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext context =
new
ClassPathXmlApplicationContext(
"test/propertyEditor/testPropertyEditor.xml"
);
Contact c = (Contact) context.getBean(
"contact"
);
System.out.println(c.getPhoneNumber().getAreaCode());
System.out.println(c.getPhoneNumber().getPrefix());
System.out.println(c.getPhoneNumber().getNumber());
}
}
ok, 很简?下一ơ来看看,Spring提供的一下比较有意思的功能.如定?发送Email{?
Spring in Action
W记
(II)
今天来看看用JDBC来操作数据: 使用的是DerbyQJavaDBQ数据库Q关于JavaDB的介l请点击q里Q?/font>
http://blog.matrix.org.cn/page/icess?catname=%2FJavaDB
?下面建立一个DatabaseUtils.java的工LQ来操作数据?。该cd上面的连接的文章中有讲述?/font>
package
test.jdbc;
import
java.io.File;
import
java.io.IOException;
import
java.io.InputStream;
import
java.sql.Connection;
import
java.sql.DriverManager;
import
java.sql.PreparedStatement;
import
java.sql.ResultSet;
import
java.sql.SQLException;
import
java.sql.Statement;
import
java.util.Properties;
import
java.util.logging.Logger;
public class
DatabaseUtils {
private static final
String DB_PROPERTIES_FILE =
"jdbc.properties"
;
private static final
String DB_OPPOSITE_LOCATION =
"/.test"
;
static
Logger logger = Logger.getLogger(DatabaseUtils.
class
.getName());
private
Connection dbConnection;
private
Properties dbProperties;
private boolean
isConnected;
// database name
private
String dbName;
private static final
String strCreateTestClobTeble =
"CREATE TABLE APP.test (id INT, name VARCHAR(30),text CLOB(64 K))"
;
private static final
String strInsertIntoTestTeble =
"INSERT INTO APP.test (id, name) VALUES (?, ?)"
;
public static final
String strGetTest =
"SELECT * FROM APP.test WHERE ID = ?"
;
private static final
String strCreateCourseTable =
"create table APP.Course ("
+
" ID INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),"
+
" name VARCHAR(30), "
+
" description VARCHAR(30), "
+
" startDate DATE, "
+
" endDate DATE "
+
")"
;
private static final
String strCreateStudentTable =
"create table APP.ADDRESS ("
+
" ID INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),"
+
" LASTNAME VARCHAR(30), "
+
" FIRSTNAME VARCHAR(30), "
+
" MIDDLENAME VARCHAR(30), "
+
" PHONE VARCHAR(20), "
+
" EMAIL VARCHAR(30), "
+
" ADDRESS1 VARCHAR(30), "
+
" ADDRESS2 VARCHAR(30), "
+
" CITY VARCHAR(30), "
+
" STATE VARCHAR(30), "
+
")"
;
public
DatabaseUtils() {
this
(
"test"
);
}
public
DatabaseUtils(String dbName) {
this
.dbName = dbName;
setDBSystemDir();
dbProperties = loadDBProperties();
String driverName = dbProperties.getProperty(
"db.driver"
);
loadDatabaseDriver(driverName);
if
(!dbExists()) {
createDatabase();
}
}
private
Properties loadDBProperties() {
InputStream dbPropInputStream =
null
;
dbPropInputStream = DatabaseUtils.
class
.getResourceAsStream(DB_PROPERTIES_FILE);
dbProperties =
new
Properties();
try
{
dbProperties.load(dbPropInputStream);
}
catch
(IOException e) {
e.printStackTrace();
}
return
dbProperties;
}
private void
setDBSystemDir() {
String userDir = System.getProperty(
"user.dir"
,
"."
);
String systemDir = userDir + DB_OPPOSITE_LOCATION;
System.setProperty(
"derby.system.home"
, systemDir);
// create the db System dir
File fileSystemDir =
new
File(systemDir);
fileSystemDir.mkdir();
}
private void
loadDatabaseDriver(String driverName) {
try
{
Class.forName(driverName);
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
}
private boolean
dbExists() {
boolean
bExists =
false
;
String dbLocation = getDatabaseLocation();
File dbFileDir =
new
File(dbLocation);
if
(dbFileDir.exists()) {
bExists =
true
;
}
return
bExists;
}
private boolean
createDatabase() {
boolean
bCreated =
false
;
Connection dbConnection =
null
;
String dbUrl = getDatabaseUrl();
dbProperties.put(
"create"
,
"true"
);
try
{
dbConnection = DriverManager.getConnection(dbUrl, dbProperties);
bCreated = createTables(dbConnection, strCreateTestClobTeble);
}
catch
(SQLException e) {
e.printStackTrace();
}
dbProperties.remove(
"create"
);
return
bCreated;
}
private boolean
createTables(Connection dbConnection, String creatTableSql) {
boolean
bCreatedTables =
false
;
Statement statement =
null
;
try
{
statement = dbConnection.createStatement();
statement.execute(creatTableSql);
bCreatedTables =
true
;
}
catch
(SQLException e) {
e.printStackTrace();
}
return
bCreatedTables;
}
public
String getDatabaseUrl() {
return
dbProperties.getProperty(
"db.url"
) + dbName;
}
public
String getDatabaseLocation() {
String dbLocation = System.getProperty(
"derby.system.home"
) +
"/"
+ dbName;
return
dbLocation;
}
public boolean
connect() {
String dbUrl = getDatabaseUrl();
try
{
logger.info(
"DBUrl: "
+ dbUrl);
dbConnection = DriverManager.getConnection(dbUrl, dbProperties);
isConnected = dbConnection !=
null
;
}
catch
(SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
isConnected =
false
;
logger.info(
"create connection if failed!"
);
}
return
isConnected;
}
public
Connection getConnection() {
return
dbConnection;
}
public void
disconnect() {
if
(isConnected) {
String dbUrl = getDatabaseUrl();
dbProperties.put(
"shutdown"
,
"true"
);
try
{
System.out.println(
"断开数据库连????????????????"
);
DriverManager.getConnection(dbUrl, dbProperties);
System.out.println(
"????????????????"
);
}
catch
(SQLException e) {
// e.printStackTrace();
logger.info(
"disconnect the connection Successful!"
);
}
isConnected =
false
;
}
}
/**
*
@param
args
*/
public static void
main(String[] args) {
// TODO Auto-generated method stub
DatabaseUtils testdb =
new
DatabaseUtils();
logger.info(testdb.getDatabaseLocation());
logger.info(testdb.getDatabaseUrl());
testdb.connect();
Connection c = testdb.getConnection();
PreparedStatement ps =
null
;
try
{
ps = c.prepareStatement(DatabaseUtils.strInsertIntoTestTeble, Statement.RETURN_GENERATED_KEYS);
ps.setInt(
1
,
1
);
ps.setString(
2
,
"test Icerain"
);
int
i =ps.executeUpdate();
System.out.println(i);
ps.close();
ps = c.prepareStatement(DatabaseUtils.strGetTest);
ps.setInt(
1
,
1
);
ResultSet rs = ps.executeQuery();
if
(rs.next()) {
String name = rs.getString(
2
);
System.out.println(name);
}
ps.close();
}
catch
(SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
testdb.disconnect();
}
}
下面是一个插入数据的c?InsertData.java
package
test.jdbc;
import
java.sql.Types;
import
javax.sql.DataSource;
import
org.springframework.jdbc.core.SqlParameter;
import
org.springframework.jdbc.object.SqlUpdate;
public class
InsertData
extends
SqlUpdate {
// 需要注入一个DataSource...
public
InsertData(DataSource ds) {
setDataSource(ds);
// TODO 注意 讄数据?/font>
setSql(
"INSERT INTO APP.test (id, name) VALUES (?, ?)"
);
declareParameter(
new
SqlParameter(Types.INTEGER));
declareParameter(
new
SqlParameter(Types.VARCHAR));
compile();
// TODO 注意 , 要编译以后才可以使用
}
// 覆盖insertҎ
public int
insert(TestData data) {
Object[] params =
new
Object[] {data.id,data.name};
return
update(params);
// 执行插入操作....
}
}
很简? q带有详l注?
下面是一个查询的c?QueryDataById.java
package
test.jdbc;
import
java.sql.ResultSet;
import
java.sql.SQLException;
import
java.sql.Types;
import
javax.sql.DataSource;
import
org.springframework.jdbc.core.SqlParameter;
import
org.springframework.jdbc.object.MappingSqlQuery;
public class
QueryDataById
extends
MappingSqlQuery{
private static final
String sql =
"SELECT * FROM APP.test WHERE ID = ?"
;
public
QueryDataById(DataSource ds) {
super
(ds,sql);
declareParameter(
new
SqlParameter(
"id"
,Types.INTEGER));
compile();
}
// 覆盖mapRowҎ
@Override
protected
Object mapRow(ResultSet rs,
int
index)
throws
SQLException {
// TODO Auto-generated method stub
TestData tdata =
new
TestData();
tdata.id = rs.getInt(
1
);
tdata.name = rs.getString(
2
);
return
tdata;
}
}
也很?
注意:
以上两个c都实现了Spring化Jdbc操作的一些接? 关于接口的信息请查考文? q里不在详细讲述.
下面是一个很单的试(数据)实体c?TestData.java
package
test.jdbc;
public class
TestData {
public int
id;
public
String name;
public
TestData(
int
id, String name) {
this
.id = id;
this
.name = name;
}
public
TestData() {}
}
下面是一个测试数据源是否注入正确的类:TestDataSource.java
package
test.jdbc;
import
java.sql.Connection;
import
java.sql.PreparedStatement;
import
java.sql.ResultSet;
import
javax.sql.DataSource;
public class
TestDataSource {
private
DataSource dataSource;
// 注入数据?/span>
public void
setDataSource(DataSource dataSource) {
this
.dataSource = dataSource;
}
//试数据?/span>
public void
testDataSource() {
try
{
System.out.println(
"Test DataSource!!!"
);
Connection connection = dataSource.getConnection();
if
(connection !=
null
)
System.out.println(
"test ok!"
);
PreparedStatement ps =
null
;
ps = connection.prepareStatement(DatabaseUtils.strGetTest);
ps.setInt(
1
,
1
);
ResultSet rs = ps.executeQuery();
if
(rs.next()) {
String name = rs.getString(
2
);
System.out.println(
"试数据源配|?"
+ name);
}
ps.close();
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
下面是测试Spring提高的Jdbc功能的主要测试类, 试了一些用JDBC操作数据的常用功? 其他没有试的请查看其Doc,TestJdbcTemplate.java
整理一下我目前可能会用到的模块, 对于那些现在Ҏ用不到的冬冬q是{有旉再研I吧!
W一个当然是最l典的HelloWorld ?, 呵呵,?但是说明了原?
定义一个服务接?/p>
package
test.helloworld;
public interface
GreetingService {
public void
sayGreeting();
}
下面是其实现:
package
test.helloworld;
public class
GreetingServiceImpl
implements
GreetingService {
private
String greeting;
public
GreetingServiceImpl() {}
public
GreetingServiceImpl(String greeting) {
this
.greeting = greeting;
}
public void
sayGreeting() {
// Auto-generated method stub
System.out.println(greeting);
}
public void
setGreeting(String greeting) {
this
.greeting = greeting;
}
}
然后是试 IoC 的测试代?
package
test.helloworld;
import
org.springframework.beans.factory.BeanFactory;
import
org.springframework.beans.factory.xml.XmlBeanFactory;
import
org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import
org.springframework.core.io.FileSystemResource;
public class
HelloApp {
/**
*
@param
args
*/
public static void
main(String[] args) {
// TODO Auto-generated method stub
// BeanFactory factory;
// factory = new XmlBeanFactory(new FileSystemResource("src/test/helloworld/hello.xml"));
// 使用不同的方法得到bean. (BeanFactory or ApplicationContext)
ApplicationContext context =
new
ClassPathXmlApplicationContext(
"test/helloworld/hello.xml"
);
GreetingService greetingService = (GreetingService) context.getBean(
"greetingService"
);
// GreetingService greetingService = (GreetingService) factory.getBean("greetingService");
greetingService.sayGreeting();
}
}
q有重要的配|文件如?hello.xml
<?
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 = "greetingService" class = "test.helloworld.GreetingServiceImpl" >
< property name = "greeting" >
< value > ice rain ! </ value >
</ property >
</ bean >
</
beans >呵呵p么简?实现?greeting 属性的 Ioc.
q是Spring 两大基本支柱其一的工作原? 关于AoP的内?在这里不作讨?因ؓ现在我用AoP的地方不是很?单的应用是很单的?^_^.
下面一我们来看看 在spring包装下的jdbc讉K. 详细情况也可?b>参考这?/a>
回顾Q?/strong>
开源框架在以前的项目中用过不少Q?struts,webwork,hibernate,前两者属于web框架Q后者属于ORM框架Q这些框架基本都是侧重于应用的某个层面,不能UC为J2EE全面的框Ӟ往往是需要和其他框架相结合,我开发的目采用q如下组合,有的Ҏ没有框Ӟ只是自己做了设计装
a、servlet+jdbc,
b、servlet+jsp+javabean+jdbc,
c、struts+BD+DAO ,
d、webwork+ejb+hibernate
使用体会Q?/strong>
a、b׃要说了,那时候的代码l构真是天马行空Q我当时初学Q维护公怸个项目代码,1个jsp实现一个模块所有功能,我花了好长时间才 L代码?br /> 时代L向前发展的,慢慢的系l结构层ơ上是越来越清晰Q开发效率也来高Q维护也来容易(Z一定的培训成本Q?br />
相对而言Z设计理念来讲Q个人更喜欢webwork+hibernate框架l合?/font>
常有论同{开发层面框架的优缺点,我所属品的另外一个项目团队曾lؓ目后箋开?应该采用strutsq是webwork争论的不可开交, 其实个h认ؓstruts,webwork都是非常好的web框架Q都有自w的优缺点:
struts开创web MVC框架之先治I2003Q?004q开发的目基本都是采用struts框架Q当时招聘h的时候常问会不会strutsQ会基本招了 )Q实际MVC设计理念很早提ZQ在j2SE中有很多使用之处Q但当时没有一个web框架开发中很好的诏彻该设计理念Q直到struts 出现Q可能有Q只是没有apache更引人注意)?br />
struts优点使用用户多,相关技术文比较全面,开发遇到的问题相关案例也比较多、比较容易解冻I框架更加E_(q一炚w帔R要)?br />
webwork相对于struts来讲Q的有不少优异之处Q(也是情理之中的事Q后发布的框架如果再没有优点别h也不会用)
个h认ؓ在开发上主要有以下几点:
1、页面数据封装成值对象功能比struts强大Qwebwork采用ognlcd转化?br /> struts只能对简单类型的数据对象以及文g对象装成值对? 而webwork装的值对象基本没有限Ӟ值对象属性还可以是List,Mapq些对象Q这个特性在我们开发一些批量数据、复杂数据处理时非常方便Q,减少很多代码量,代码非常整洁?br />
2、拦截器功能
拦截器是webwork的一个亮点,实际上也是业界比较流行的AOP的一个体玎ͼ在实际开发中你可以配|默认的拦截器, 也可以ؓ单独的模块指定特定的拦截器,q且可自定义拦截器,action执行前后拦截都可以。?br />
3、单元测试比较方?br /> 最早用struts框架开发测试时Q相关的单元试基本是不好做的,无法qweb环境Q测试都是只做集成测试、系l测试?br /> 使用webwork可以单对action相关属性赋|可以相关的单元测试,当然前提是在对应action中没有引用web环境相关的对象。?br /> 其实webwork框架核心q是xwork框架Q最早框架用在C/Sl构下,webwork只是xwork的一个在web环境的实玎ͼ只是ActionContext 上下文发生了变化Q所以说action能够做到qweb环境也是情理之中的?/p>
4、配|文?br />webwork配置文g可以采用引用、承其他配|文件方式,在团队开发一般都是分模块开发,q样比较方便Q配|管理更ҎQ不会冲H?br />
5、模板技术集?br /> 我在实际应用目中是采用velocity模板做视囑ֱ玎ͼ因ؓ在对应版本的webwork框架中和velociy集成的相当好Q比较方便,
比直接写jsp代码更整z、同时利用velocity模板Ҏ结合每个action的配|文Ӟ可实现比较通用的页面查询、录入等视图的展现?br /> 而struts是没有相x杉K成,不过struts的tag相对而言比webwork的tag好用Qwebwork如果视图是jspcdQ相关的tag真的比较ȝ
虽然tag 库很丰富Q这也是我ؓ什么用velocity做视囄原因 。?br />
6?框架的效验功?br /> 老实_两者框架的效验功能都比较麻烦,相对而言webwork更加ȝ点,配置较多Q验证接口实现太ȝQ我实际目使用中还?br /> 自定义了相关后台验证接口Q?要验证的相关action只要实现相关接口卛_Q相x截器负责拦截验证Q?br /> 大部分的效验Ҏ配置数据以及html对象自定义属性通过javascript通用效验Q实际上现在ZXmlHttp的ajax技术应用成熟的?br /> 后台验证接口的用途会逐渐淡化?
说明上述比较版本?struts 1.0 /webwork 2.1.6Q后lstruts框架扩展很多功能Q不是很了解Q可能和上诉描述不一定很d?br />
现在两者已l合qӞ希望能结合两者的优势Q发展出更好web框架?br />
7、关于Hibernateq是DAO模式Q个人徏议采用hibernate+DAO盔R合的模式Qhibernate占主导地位?br /> 虽然DAO模式通过自动生成代码效率不会低,但如果需求变更就比较ȝQ维护修改代码较多,试工作量也较大Q 但Hibernate不是万能的,在一些必要的应用q是采用DAO模式Q特别是性能相关的部分?
废话一大堆Q也把我的工作回顾了一遍,呵呵Q这两天单了解Spring框架Q个人感觉Spring更像一个全面的J2EE框架解决Ҏ, 希望能够有时间系l的学习一把,大家有兴的请多多交?我会我的学习心得和大家分n?/p>