??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新的状态。它们仅仅作用于Ҏ(gu)和方法的参数?/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使用Ҏ(gu)拦截器的around通知是和AOP联盟接口兼容的。实?span>around通知?c需要实现接?span>MethodInterceptorQ?/span>
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
invoke()Ҏ(gu)?em>MethodInvocation 参数暴露被调用的方法、目标连接点?span>AOP代理和传递给被调用方法的参数?invoke()Ҏ(gu)应该q回调用的结果:(x)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()Ҏ(gu)的调用。这个调用会(x)应用到目标连接点的拦截器链中的每一个拦截器。大部分拦截器会(x)调用q个Ҏ(gu)Qƈq回它的q回倹{但是, 一?span>MethodInterceptorQ和Maround通知一P可以q回不同的值或者抛Z个异常,?不调?span>proceedҎ(gu)。但是,没有好的原因你要q么做?/span>
MethodInterceptor提供?jin)和其?span>AOP联盟的兼容实现的交互能力。这一节下?要讨论的其他的通知cd实现?span>AOP公共的概念,但是?span>Spring特定的方式。虽然用特?通知cd有很多优点,但如果你可能需要在其他?span>AOP框架中用,请坚持?span>MethodInterceptor around通知cd。注意目前切入点不能和其它框架交互操作,q且AOP联盟目前也没有定义切?Ҏ(gu)口?/span>
Before通知是一U简单的通知cd?q个通知不需要一?span>MethodInvocation对象Q因为它只在q入一个方法前被调用?/span>
Before通知的主要优Ҏ(gu)它不需要调?span>proceed() Ҏ(gu)Q?因此没有无意中忘掉l执行拦截器铄可能性?/span>
MethodBeforeAdvice接口如下所C?span> (Spring?span>API设计允许成员变量?span>before通知Q虽然一般的对象都可以应用成员变量拦截,?span>Spring 有可能永q不?x)实现它Q?/span>
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
注意q回cd?span>void?span> Before通知可以在连接点执行之前 插入自定义的行ؓ(f)Q但是不能改变返回倹{如果一?span>before通知抛出一个异常,q将中断拦截?铄q一步执行。这个异常将沿着拦截器链后退着向上传播。如果这个异常是unchecked的,或?出现在被调用的方法的{中,它将?x)被直接传递给客户代码Q否则,它将?span>AOP代理包装C?span>unchecked 的异帔R?/span>
下面?span>Spring中一?span>before通知的例子,q个例子计数所有正常返回的Ҏ(gu)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接Ҏ(gu)出异常,Throws通知 在连接点q回后被调用?span>Spring提供强类型的throws通知。注意这意味着 org.springframework.aop.ThrowsAdvice接口不包含Q何方法:(x) 它是一个标记接口,标识l定的对象实C(jin)一个或多个强类型的throws通知Ҏ(gu)。这些方法Ş?如下Q?/span>
afterThrowing([Method], [args], [target], subclassOfThrowable)
只有最后一个参数是必需的。这样从一个参数到四个参数Q依赖于通知是否Ҏ(gu)法和Ҏ(gu) 的参数感兴趣。下面是throws通知的例子?/span>
如果抛出RemoteException异常Q包括子c)(j), q个通知?x)被调?/span>
public class RemoteThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
}
如果抛出ServletException异常Q?下面的通知?x)被调用。和上面的通知不一P它声明了(jin)四个参数Q所以它可以讉K被调用的Ҏ(gu)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(jin)如何在一个类中用两个方法来同时处理 RemoteException?span>ServletException 异常。Q意个数的throwsҎ(gu)可以被组合在一个类中?/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:(x)
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计所有成功的没有抛出异常的方法调用:(x)
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()Ҏ(gu)必须实现导入Q也是_(d)如果被调用的Ҏ(gu)是在 导入的接口中Q导入拦截器负责处理q个Ҏ(gu)调用Q它不能调用proceed() Ҏ(gu)?/span>
Introduction通知不能被用于Q何切入点Q因为它只能作用于类层次上,而不是方法。你可以只用InterceptionIntroductionAdvisor来实现导入通知Q它有下面的Ҏ(gu)Q?/span>
public interface InterceptionIntroductionAdvisor extends InterceptionAdvisor {
ClassFilter getClassFilter();
IntroductionInterceptor getIntroductionInterceptor();
Class[] getInterfaces();
}
q里没有MethodMatcherQ因此也没有和导入通知兌?切入炏V只有类qo(h)是合乎逻辑的?/span>
getInterfaces()Ҏ(gu)q回advisor导入的接口?/span>
让我们看看一个来?span>Spring试套g中的单例子。我们假设想要导入下面的接口C?或者多个对象中:
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
q个例子演示?jin)一?span>mixin。我们想要能?被通知对象cd转换?span>LockableQ不它们的cdQƈ且调?span>lock?span>unlockҎ(gu)。如果我们调?span> lock()Ҏ(gu)Q我们希望所?span>setterҎ(gu)抛出LockedException异常。这h们能d一个方面的对象不可变Q而它们不需要知道这一点:(x)q是一个很好的AOP?子?/span>
首先Q我们需要一个做大量转化?span>IntroductionInterceptor?在这里,我们l承 org.springframework.aop.support.DelegatingIntroductionInterceptor 实用cR我们可以直接实?span>IntroductionInterceptor接口Q但是大多数情况?span> DelegatingIntroductionInterceptor是最合适的?/span>
DelegatingIntroductionInterceptor的设计是导?委托到真正实现导入接口的接口Q隐藏完成这些工作的拦截器。委托可以用构造方法参?讄CQ何对象中Q默认的委托是自己Q当无参数的构造方法被使用Ӟ(j)。这样在下面的例子里Q委托是DelegatingIntroductionInterceptor的子c?span> LockMixin。给定一个委托(默认是自w)(j)?span> DelegatingIntroductionInterceptor实例L被这个委托(而不 ?span>IntroductionInterceptorQ实现的所有接口,q支持它们中M一个导入。子cd LockMixin也可能调?span>suppressInterflace(Class intf) Ҏ(gu)隐藏不应暴露的接口。然而,不管IntroductionInterceptor 准备支持多少接口Q?span>IntroductionAdvisor控制哪个接口将被实?暴露。一个导入的接口隐藏目标的同一个接口的所有实现?/span>
q样Q?span>LockMixinl承DelegatingIntroductionInterceptor q自己实?span>Lockable。父c自动选择支持导入?span>LockableQ所以我们不需要指定它。用q种Ҏ(gu)我们可以导入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()Ҏ(gu)Q实?span> DelegatingIntroductionInterceptorp够了(jin)Q如果是导入的方法, DelegatingIntroductionInterceptor实现?x)调用委托方法?否则l箋(hu)沿着q接点处理。在现在的情况下Q我们需要添加一个检查:(x)在上锁状态下不能调用setterҎ(gu)?/span>
所需的导?span>advisor是很单的。只有保存(sh)个独立的 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>必要的:(x)是不可能在没有IntroductionAdvisor 的情况下使用IntroductionInterceptor。)(j) 和导入一P通常 advisor必须是针Ҏ(gu)个实例的Qƈ且是有状态的。我们会(x)有不同的?span>LockMixinAdvisor 每个被通知对象Q会(x)有不同的LockMixin?span> advisorl成?jin)被通知对象的状态的一部分?/span>
和其?span>advisor一P我们可以使用 Advised.addAdvisor() Ҏ(gu)以编E地方式使用q种advisorQ或者在XML中配|(推荐q种方式Q?下面讨论所有代理创建,包括“自动代理创徏?span>”Q选择代理创徏以正地处理导入和有状态的混入?/span>
参考资料:(x)
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> 错,改ؓ(f)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> |
(tng) (tng) (tng) 它有两种配置方式OpenSessionInViewInterceptor OpenSessionInViewFilter(具体参看SpringSide) web.xml application.xml (tng) (tng) (tng) Open Session In View request session thread hibernate session open session request View PO lazy loading ${ 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则等于没?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程中提?qing)一个错误:(x)
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里的几个Ҏ(gu)
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);
}
}
(tng) 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);
}
(tng) (tng) (tng) (tng) 可以看到OpenSessionInViewFilter在getSession的时??x)把获取回来的session的flush mode 设ؓ(f)FlushMode.NEVER。然后把该sessionFactoryl定到TransactionSynchronizationManagerQrequest的整个过E都使用同一个sessionQ在hq后再接除该sessionFactory的绑定,最?span class="me1">closeSessionIfNecessaryҎ(gu)该session是否已和transactionl定来决定是否关闭session。在q个q程中,若HibernateTemplate 发现自当前session有不是readOnly的transactionQ就?x)获取到FlushMode.AUTO SessionQҎ(gu)拥有写权限?/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); } }
(tng) (tng) (tng) 也即是,如果有不是readOnly的transaction可以由Flush.NEVER转ؓ(f)Flush.AUTO,拥有insert,update,delete操作权限Q如果没有transactionQƈ且没有另外h为地设flush model的话Q则doFilter的整个过E都是Flush.NEVER。所以受transaction保护的方法有写权限,没受保护的则没有?/p>
采用spring的事务声?使方法受transaction控制
(tng) <bean id="baseTransaction"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
(tng) (tng) (tng) (tng) (tng) abstract="true">
(tng) (tng) (tng) (tng) <property name="transactionManager" ref="transactionManager"/>
(tng) (tng) (tng) (tng) <property name="proxyTargetClass" value="true"/>
(tng) (tng) (tng) (tng) <property name="transactionAttributes">
(tng) (tng) (tng) (tng) (tng) (tng) <props>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop key="save*">PROPAGATION_REQUIRED</prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop key="add*">PROPAGATION_REQUIRED</prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop key="update*">PROPAGATION_REQUIRED</prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop key="remove*">PROPAGATION_REQUIRED</prop>
(tng) (tng) (tng) (tng) (tng) (tng) </props>
(tng) (tng) (tng) (tng) </property>
(tng) (tng) </bean>
- (tng) (tng) <bean id="userService" parent="baseTransaction">
(tng) (tng) (tng) (tng) <property name="target">
(tng) (tng) (tng) (tng) (tng) (tng) <bean class="com.phopesoft.security.service.impl.UserServiceImpl"/>
(tng) (tng) (tng) (tng) </property>
(tng) (tng) </bean>
对于上例Q则以save,add,update,remove开头的Ҏ(gu)拥有可写的事务,如果当前有某个方法,如命名ؓ(f)importExcel()Q则因没有transaction而没有写权限Q这时若Ҏ(gu)内有insert,update,delete操作的话Q则需要手动设|flush model为Flush.AUTO,?/p>
session.setFlushMode(FlushMode.AUTO); session.save(user); session.flush();
(tng) (tng) (tng) (tng) (tng)管Open Session In View看v来还?sh)错Q其实副作用不少。看回上面OpenSessionInViewFilter的doFilterInternalҎ(gu)代码Q这个方法实际上是被父类的doFilter调用的,因此Q我们可以大U了(jin)解的OpenSessionInViewFilter调用程: request(h)->open sessionq开始transaction->controller->View(Jsp)->l束transactionqclose session.
(tng) (tng) (tng) (tng) 一切看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装的完无?哈哈
试牛刀开始了(jin)...
java代码呢就是区区几?
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.apache.log4j.*;
public class ContentArbitrateServiceJob extends QuartzJobBean {
(tng)public static Logger log = Logger.getLogger(ContentArbitrateServiceJob.class);
(tng)private int timeout;
(tng)public void setTimeout(int timeout){
(tng) (tng)this.timeout = timeout;
(tng)}
(tng)protected void executeInternal(JobExecutionContext arg0)
(tng) (tng) (tng)throws JobExecutionException {
(tng) (tng)// TODO Auto-generated method stub
(tng) (tng)try{
(tng) (tng) (tng)log.info("pȝ监督仲裁处理d开?gt;........");
(tng) (tng) (tng)//业务逻辑代码调用
(tng) (tng) (tng)log.info("pȝ监督仲裁处理dl束!");
(tng) (tng)}catch(Exception e){
(tng) (tng) (tng)log.error("pȝ监督仲裁处理d出现异常",e);
(tng) (tng)}
(tng)}
} (tng)
(tng)
下面是看配|文仉面的道道?其实也不?
<bean name="contentarbitrateservicejob" (tng) class="org.springframework.scheduling.quartz.JobDetailBean">
(tng) (tng) (tng)<property name="jobClass">
(tng) (tng) (tng) (tng)<value>com.xuedu.ContentArbitrateServiceJob</value>
(tng) (tng) (tng)</property>
(tng) (tng) (tng)<property name="jobDataAsMap">
(tng) (tng) (tng) (tng)<map>
(tng) (tng) (tng) (tng) (tng)<entry key="timeout">
(tng) (tng) (tng) (tng) (tng) (tng)<value>5</value>
(tng) (tng) (tng) (tng) (tng)</entry>
(tng) (tng) (tng) (tng)</map>
(tng) (tng) (tng)</property>
(tng) (tng) </bean>
(tng)
(tng) <!-- 配置触发?-->
(tng) (tng) (tng) (tng) (tng) (tng) <bean id="cronTriggerCA" class="org.springframework.scheduling.quartz.CronTriggerBean">
(tng) (tng)<property name="jobDetail">
(tng) (tng) (tng)<ref bean="contentarbitrateservicejob"/>
(tng) (tng)</property>
(tng) (tng)<!-- 每天?点到21??0分钟触发Q具体说明见附录 -->
(tng) (tng)<property name="cronExpression">
(tng) (tng) (tng)<value>0 50 08-21 * * ?</value>
(tng) (tng)</property>
(tng) (tng) (tng) </bean>
(tng) (tng) (tng)
(tng) (tng) (tng) <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
(tng) (tng)<!-- d触发?-->
(tng) (tng) <property name="triggers">
(tng) (tng) (tng) <list>
(tng) (tng) (tng)<ref local="cronTriggerCA"/>
(tng) (tng) (tng) </list>
(tng) (tng) </property>
(tng) (tng) (tng) </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_(d)可选)(j) 留空, 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号 看看例子好?很简单了(jin).
Acegi (tng)的配|看h非常复杂,但事实上在实际项目的安全应用中我们ƈ不需要那么多功能,清楚的了(jin)解Acegi配置中各的功能Q有助于我们灉|的运用Acegi于实践中。?/font>
(tng)
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)Acegi (tng)的配|看h非常复杂,但事实上在实际项目的安全应用中我们ƈ不需要那么多功能,清楚的了(jin)解Acegi配置中各的功能Q有助于我们灉|的运用Acegi于实践中?/font>
2.1 (tng)在Web.xml中的配置
1) (tng) (tng)FilterToBeanProxy
Acegi通过实现?jin)Filter接口的FilterToBeanProxy提供一U特D的使用Servlet (tng)Filter的方式,它委托Spring中的Bean (tng)-- (tng)FilterChainProxy来完成过滤功能,q好处是化了(jin)web.xml的配|,q且充分利用?jin)Spring (tng)I(yng)OC的优ѝFilterChainProxy包含?jin)处理认证过E的filter列表Q每个filter都有各自的功能?/font>
(tng) (tng) (tng) (tng)<filter>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<filter-name>Acegi (tng)Filter (tng)Chain (tng)Proxy</filter-name>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<init-param>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<param-name>targetClass</param-name>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</init-param>
(tng) (tng) (tng) (tng)</filter>
2) (tng)filter-mapping
<filter-mapping>限定?jin)FilterToBeanProxy的URL匚w模式,只有*.do?.jsp?j_acegi_security_check (tng)的请求才?x)受到权限控Ӟ对javascript,css{不限制?/font>
(tng) (tng) (tng)<filter-mapping>
(tng) (tng) (tng) (tng) (tng) (tng)<filter-name>Acegi (tng)Filter (tng)Chain (tng)Proxy</filter-name>
(tng) (tng) (tng) (tng) (tng) (tng)<url-pattern>*.do</url-pattern>
(tng) (tng) (tng) (tng)</filter-mapping>
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)<filter-mapping>
(tng) (tng) (tng) (tng) (tng) (tng)<filter-name>Acegi (tng)Filter (tng)Chain (tng)Proxy</filter-name>
(tng) (tng) (tng) (tng) (tng) (tng)<url-pattern>*.jsp</url-pattern>
(tng) (tng) (tng) (tng)</filter-mapping>
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)<filter-mapping>
(tng) (tng) (tng) (tng) (tng) (tng)<filter-name>Acegi (tng)Filter (tng)Chain (tng)Proxy</filter-name>
(tng) (tng) (tng) (tng) (tng) (tng)<url-pattern>/j_acegi_security_check</url-pattern>
(tng) (tng) (tng) (tng)</filter-mapping>
3) (tng)HttpSessionEventPublisher
<listener>的HttpSessionEventPublisher用于发布HttpSessionApplicationEvents和HttpSessionDestroyedEvent事glspring的applicationcontext?/font>
(tng) (tng) (tng) (tng)<listener>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
(tng) (tng) (tng) (tng)</listener>
FilterChainProxy?x)按序来调用这些filter,使这些filter能n用Spring (tng)ioc的功? (tng)CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义?jin)url比较前先转ؓ(f)写Q?tng)PATTERN_TYPE_APACHE_ANT定义?jin)用Apache (tng)ant的匹配模式?/font>
(tng) (tng) (tng) (tng)<bean (tng)id="filterChainProxy" (tng)class="org.acegisecurity.util.FilterChainProxy">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="filterInvocationDefinitionSource">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)PATTERN_TYPE_APACHE_ANT
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,
basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,
(tng)exceptionTranslationFilter,filterInvocationInterceptor
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</property>
(tng) (tng) (tng) (tng)</bean>
1) (tng)authenticationManager
起到认证理的作用,它将验证的功能委托给多个ProviderQƈ通过遍历Providers, (tng)以保证获取不同来源的w䆾认证Q若某个Provider能成功确认当前用L(fng)w䆾Qauthenticate()Ҏ(gu)?x)返回一个完整的包含用户授权信息的Authentication对象Q否则会(x)抛出一个AuthenticationException?br />Acegi提供?jin)不同的AuthenticationProvider的实?如:(x)
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)DaoAuthenticationProvider (tng)从数据库中读取用户信息验证n?br /> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)AnonymousAuthenticationProvider (tng)匿名用户w䆾认证
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)RememberMeAuthenticationProvider (tng)已存cookie中的用户信息w䆾认证
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)AuthByAdapterProvider (tng)使用容器的适配器验证n?br /> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)CasAuthenticationProvider (tng)Ҏ(gu)Yale中心(j)认证服务验证?li)w䆾, (tng)用于实现单点登陆
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)JaasAuthenticationProvider (tng)从JASS登陆配置中获取用户信息验证n?br /> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)RemoteAuthenticationProvider (tng)Ҏ(gu)q程服务验证用户w䆾
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)RunAsImplAuthenticationProvider (tng)对n份已被管理器替换的用戯行验?br /> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)X509AuthenticationProvider (tng)从X509认证中获取用户信息验证n?br /> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)TestingAuthenticationProvider (tng)单元试时?/font>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)每个认证者会(x)对自己指定的证明信息q行认证Q如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationTokenq个证明信息q行认证?/font>
<bean (tng)id="authenticationManager" (tng)class="org.acegisecurity.providers.ProviderManager">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="providers">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<list>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<ref (tng)local="daoAuthenticationProvider"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<ref (tng)local="anonymousAuthenticationProvider"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<ref (tng)local="rememberMeAuthenticationProvider"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</list>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</property>
</bean>
2) (tng)daoAuthenticationProvider
q行单的Z数据库的w䆾验证。DaoAuthenticationProvider获取数据库中的̎号密码ƈq行匚wQ若成功则在通过用户w䆾的同时返回一个包含授权信息的Authentication对象Q否则n份验证失败,抛出一个AuthenticatiionException?/font>
(tng) (tng) (tng) (tng)<bean (tng)id="daoAuthenticationProvider" (tng)class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="userDetailsService" (tng)ref="jdbcDaoImpl"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="userCache" (tng)ref="userCache"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="passwordEncoder" (tng)ref="passwordEncoder"/>
(tng) (tng) (tng)</bean>
3) (tng)passwordEncoder (tng)
使用加密器对用户输入的明文进行加密。Acegi提供?jin)三U加密器:
PlaintextPasswordEncoder—默认,不加密,q回明文.
ShaPasswordEncoder—哈希算?SHA)加密
Md5PasswordEncoder—消息摘?MD5)加密
<bean (tng)id="passwordEncoder" (tng)class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>
4) (tng)jdbcDaoImpl (tng)
用于在数据中获取用户信息。?tng)acegi提供?jin)用户?qing)授权的表l构Q但是?zhn)也可以自己来实现。通过usersByUsernameQueryq个SQL得到你的(用户ID,密码,状态信?;通过authoritiesByUsernameQueryq个SQL得到你的(用户ID,授权信息)
(tng) <bean (tng)id="jdbcDaoImpl" (tng) class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <property (tng)name="dataSource" (tng) ref="dataSource"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng) name="usersByUsernameQuery">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <value>select (tng)loginid,passwd,1 (tng)from (tng)users (tng)where (tng)loginid (tng)= (tng) ?</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) </property>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng) name="authoritiesByUsernameQuery">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <value>select (tng)u.loginid,p.name (tng)from (tng)users (tng)u,roles (tng)r,permissions (tng) p,user_role (tng)ur,role_permis (tng)rp (tng)where (tng)u.id=ur.user_id (tng)and (tng)r.id=ur.role_id (tng)and (tng) p.id=rp.permis_id (tng) and
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) r.id=rp.role_id (tng)and (tng)p.status='1' (tng)and (tng) u.loginid=?</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) </property>
</bean>
5) (tng)userCache & (tng) (tng)resourceCache (tng)
~存用户和资源相对应的权限信息。每当请求一个受保护资源ӞdaoAuthenticationProvider׃(x)被调用以获取用户授权信息。如果每ơ都从数据库获取的话Q那代h(hun)很高Q对于不常改变的用户和资源信息来_(d)最好是把相x(chng)权信息缓存v来?详见 (tng)
2.6.3 (tng)资源权限定义扩展
(tng))
userCache提供?jin)两U实? (tng)NullUserCache和EhCacheBasedUserCache, (tng)NullUserCache实际上就是不q行M~存QEhCacheBasedUserCache是用Ehcache来实现缓功能?/font>
<bean (tng)id="userCacheBackend" (tng)class="org.springframework.cache.ehcache.EhCacheFactoryBean">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <property (tng)name="cacheManager" (tng)ref="cacheManager"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <property (tng)name="cacheName" (tng)value="userCache"/>
(tng) (tng) (tng) (tng)</bean>
(tng) (tng) (tng) (tng)<bean (tng)id="userCache" (tng)class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache" (tng)autowire="byName">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="cache" (tng)ref="userCacheBackend"/> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</bean>
(tng) (tng) (tng) (tng)<bean (tng)id="resourceCacheBackend" (tng)class="org.springframework.cache.ehcache.EhCacheFactoryBean"> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <property (tng)name="cacheManager" (tng)ref="cacheManager"/> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="cacheName" (tng)value="resourceCache"/>
(tng) (tng) (tng) (tng)</bean>
(tng) (tng) (tng) (tng)<bean (tng)id="resourceCache" (tng)class="org.springside.modules.security.service.acegi.cache.ResourceCache" (tng)autowire="byName">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="cache" (tng)ref="resourceCacheBackend"/>
(tng) (tng) (tng) (tng)</bean>
6) (tng)basicProcessingFilter (tng)
用于处理HTTP头的认证信息Q如从Springq程协议(如Hessian和Burlap)或普通的览器如IE,Navigator的HTTP头中获取用户信息Q将他们转交l通过authenticationManager属性装配的认证理器。如果认证成功,?x)将一个Authentication对象攑ֈ?x)话中,否则Q如果认证失败,?x)将控制转交l认证入口点(通过authenticationEntryPoint属性装?
(tng) (tng) (tng) (tng)<bean (tng)id="basicProcessingFilter" (tng)class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="authenticationManager" (tng)ref="authenticationManager"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="authenticationEntryPoint" (tng)ref="basicProcessingFilterEntryPoint"/>
(tng) (tng) (tng) (tng)</bean>
7) (tng)basicProcessingFilterEntryPoint (tng)
通过向浏览器发送一个HTTP401(未授?消息Q提C用L(fng)录?br />处理ZHTTP的授权过E, (tng)在当验证q程出现异常后的"d"Q通常实现转向、在response里加入error信息{功能?/font>
<bean (tng)id="basicProcessingFilterEntryPoint" (tng)class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint"> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <property (tng)name="realmName" (tng)value="SpringSide (tng)Realm"/>
</bean>
8) (tng)authenticationProcessingFilterEntryPoint (tng)
当抛出AccessDeniedExceptionӞ用户重定向到登录界面。属性loginFormUrl配置?jin)一个登录表单的URL,当需要用L(fng)录时QauthenticationProcessingFilterEntryPoint?x)将用户重定向到该URL
(tng) <bean (tng)id="authenticationProcessingFilterEntryPoint" (tng)class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property (tng)name="loginFormUrl">
<value>/security/login.jsp</value>
</property>
<property (tng)name="forceHttps" (tng)value="false"/>
</bean>
1) (tng)httpSessionContextIntegrationFilter
每次request前?tng)HttpSessionContextIntegrationFilter从Session中获取Authentication对象Q在request完后, (tng)又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi (tng)filter前用,使之能跨多个请求?/font>
<bean (tng)id="httpSessionContextIntegrationFilter" (tng)class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"></bean>
(tng) (tng) (tng) (tng)<bean (tng)id="httpRequestAccessDecisionManager" (tng)class="org.acegisecurity.vote.AffirmativeBased">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="allowIfAllAbstainDecisions" (tng)value="false"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="decisionVoters">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<list>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<ref (tng)bean="roleVoter"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</list>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</property>
</bean>
2) (tng)httpRequestAccessDecisionManager
l过投票机制来决定是否可以访问某一资源(URL或方?。allowIfAllAbstainDecisions为false时如果有一个或以上的decisionVoters投票通过,则授权通过。可选的决策机制有ConsensusBased和UnanimousBased
(tng) (tng) (tng) (tng)<bean (tng)id="httpRequestAccessDecisionManager" (tng)class="org.acegisecurity.vote.AffirmativeBased">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="allowIfAllAbstainDecisions" (tng)value="false"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="decisionVoters">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<list>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<ref (tng)bean="roleVoter"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</list>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</property>
(tng) (tng) (tng) (tng)</bean>
3) (tng)roleVoter
(tng) 必须是以rolePrefix讑֮的value开头的权限才能q行投票,如AUTH_ (tng), (tng)ROLE_
(tng) (tng) (tng) (tng)<bean (tng)id="roleVoter" (tng)class="org.acegisecurity.vote.RoleVoter">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="rolePrefix" (tng)value="AUTH_"/>
(tng) (tng) (tng)</bean>
4Q?strong>exceptionTranslationFilter
异常转换qo(h)器,主要是处理AccessDeniedException和AuthenticationExceptionQ将l每个异常找到合适的"d" (tng)
(tng) (tng) (tng)<bean (tng)id="exceptionTranslationFilter" (tng)class="org.acegisecurity.ui.ExceptionTranslationFilter">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="authenticationEntryPoint" (tng)ref="authenticationProcessingFilterEntryPoint"/>
(tng) (tng) (tng) (tng)</bean>
5) (tng)authenticationProcessingFilter
和servlet (tng)spec差不?处理登陆h.当n份验证成功时QAuthenticationProcessingFilter?x)在会(x)话中放|一个Authentication对象Qƈ且重定向到登录成功页?br /> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)authenticationFailureUrl定义登陆p|时{向的面
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)defaultTargetUrl定义登陆成功时{向的面
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)filterProcessesUrl定义登陆h的页?br /> (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)rememberMeServices用于在验证成功后dcookie信息
(tng) (tng) (tng) (tng)<bean (tng)id="authenticationProcessingFilter" (tng)class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="authenticationManager" (tng)ref="authenticationManager"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="authenticationFailureUrl">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<value>/security/login.jsp?login_error=1</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</property>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="defaultTargetUrl">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<value>/admin/index.jsp</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</property>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="filterProcessesUrl">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<value>/j_acegi_security_check</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)</property>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="rememberMeServices" (tng)ref="rememberMeServices"/>
(tng) (tng) (tng) (tng)</bean>
6) (tng)filterInvocationInterceptor
在执行{向url前检查objectDefinitionSource中设定的用户权限信息。首先,objectDefinitionSource中定义了(jin)讉KURL需要的属性信?q里的属性信息仅仅是标志Q告诉accessDecisionManager要用哪些voter来投?。然后,authenticationManager掉用自己的provider来对用户的认证信息进行校验。最后,有投者根据用h有认证和讉Kurl需要的属性,调用自己的voter来投,军_是否允许讉K?/font>
(tng) (tng) (tng) (tng)<bean (tng)id="filterInvocationInterceptor" (tng)class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="authenticationManager" (tng)ref="authenticationManager"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="accessDecisionManager" (tng)ref="httpRequestAccessDecisionManager"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="objectDefinitionSource" (tng)ref="filterDefinitionSource"/>
(tng) (tng) (tng) (tng)</bean>
7) (tng)filterDefinitionSource (tng)(详见 (tng)
2.6.3 (tng)资源权限定义扩展
)
自定义DBFilterInvocationDefinitionSource从数据库和cache中读取保护资源及(qing)光要的讉K权限信息 (tng)
<bean (tng)id="filterDefinitionSource" (tng)class="org.springside.modules.security.service.acegi.DBFilterInvocationDefinitionSource">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="convertUrlToLowercaseBeforeComparison" (tng)value="true"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="useAntPath" (tng)value="true"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="acegiCacheManager" (tng)ref="acegiCacheManager"/>
</bean>
(详见 (tng) 2.6.3 (tng)资源权限定义扩展 )
1) (tng)methodSecurityInterceptor
在执行方法前q行拦截Q检查用h限信?br />2) (tng)methodDefinitionSource
自定义MethodDefinitionSource从cache中读取权?/font>
(tng) (tng) (tng)<bean (tng)id="methodSecurityInterceptor" (tng)class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="authenticationManager" (tng)ref="authenticationManager"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="accessDecisionManager" (tng)ref="httpRequestAccessDecisionManager"/>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="objectDefinitionSource" (tng)ref="methodDefinitionSource"/>
(tng) (tng) (tng) (tng)</bean>
(tng) (tng) (tng) (tng)<bean (tng)id="methodDefinitionSource" (tng)class="org.springside.modules.security.service.acegi.DBMethodDefinitionSource">
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)<property (tng)name="acegiCacheManager" (tng)ref="acegiCacheManager"/>
(tng) (tng) (tng) (tng)</bean>
采用 (tng)
http://jcaptcha.sourceforge.net
(tng)作ؓ(f)通用的验证码Ҏ(gu)Q请参考SpringSide中的例子Q或|上的:(x)
http://www.coachthrasher.com/page/blog?entry=jcaptcha_with_appfuse
?/font>
差沙在此q程中又发现acegi (tng)logout (tng)filter的错误,q行?jin)修正?/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 (tng)id= "MySessionFactory" (tng) class = "org.springframework.orm.hibernate.LocalSessionFactoryBean" >
(tng) (tng) (tng) <property (tng)name= "mappingResources" >
(tng) (tng) (tng) (tng) (tng) (tng) <list>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <value>mappings/Book.hbm.xml</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <value>mappings/Patron.hbm.xml</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <value>mappings/BorrowRecord.hbm.xml</value>
(tng) (tng) (tng) (tng) (tng) (tng) </list>
(tng) (tng) (tng) </property>
(tng) (tng) (tng) <property (tng)name= "hibernateProperties" >
(tng) (tng) (tng) (tng) (tng) (tng) <props>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop (tng)key= "hibernate.dialect" >net.sf.hibernate.dialect.MySQLDialect</prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop (tng)key= "hibernate.query.substitutions" > true = 1 (tng) false = 0 </prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop (tng)key= "hibernate.show_sql" > false </prop>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <prop (tng)key= "hibernate.use_outer_join" > false </prop>
(tng) (tng) (tng) (tng) (tng) (tng) </props>
(tng) (tng) (tng) </property>
(tng) (tng) (tng) <property (tng)name= "dataSource" ><ref (tng)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 (tng) net.sf.hibernate.SessionFactory;
....
public (tng)class (tng) MyBusinessObjectImpl (tng) implements (tng) MyBusinessObject
{
(tng) (tng) (tng) private (tng) SessionFactory (tng)sessionFactory;
(tng) (tng)
(tng)
public (tng) void (tng) setSessionFactory(SessionFactory (tng)sessionFactory)
(tng) (tng) (tng) {
(tng) (tng) (tng) (tng) (tng) (tng) this .sessionFactory (tng)= (tng)sessionFactory;
(tng) (tng) (tng) }
(tng) (tng) (tng) public (tng) SessionFactory (tng)getSessionFactory()
(tng) (tng) (tng) {
(tng) (tng) (tng) (tng) (tng) (tng) return (tng)this .sessionFactory;
(tng) (tng) (tng) }
....
and hook it up to Spring.
<bean (tng)id= "MyBusinessObject" (tng) class = "library.MyBusinessObjectImpl" >
(tng) (tng) (tng) <property (tng)name= "sessionFactory" >
(tng) (tng) (tng) (tng) (tng) (tng) <ref (tng)bean= "MySessionFactory" />
(tng) (tng) (tng) </property>
</bean>
(tng)
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 (tng)id= "MyTransactionManager" (tng) 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 (tng)id= "MyTransactionInterceptor" (tng)
class = "org.springframework.transaction.interceptor.TransactionInterceptor" >
(tng) (tng) (tng) <property (tng)name= "transactionManager" ><ref (tng)bean= "MyTransactionManager" /></property>
(tng) (tng) (tng) <property (tng)name= "transactionAttributeSource" >
(tng) (tng) <value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) library.MyBusinessImpl.borrowBook=PROPAGATION_REQUIRED
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) library.MyBusinessImpl.returnBook=PROPAGATION_REQUIRED
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) library.BookSearchImpl.*=PROPAGATION_SUPPORTS
(tng) (tng) </value>
(tng) (tng) (tng) </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 (tng)id= "MyBusinessObjectTarget" (tng) class = "library.MyBusinessObjectImpl" >
(tng) (tng) (tng) <property (tng)name= "sessionFactory" >
(tng) (tng) (tng) (tng) (tng) (tng) <ref (tng)bean= "MySessionFactory" />
(tng) (tng) (tng) </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 (tng)id= "MyBusinessObject" (tng) class = "org.springframework.aop.framework.ProxyFactoryBean" >
(tng) (tng) (tng) <property (tng)name= "proxyInterfaces" >
(tng) (tng) (tng) (tng) (tng) (tng) <value>library.MyBusinessObject</value>
(tng) (tng) (tng) </property>
(tng) (tng) (tng) <property (tng)name= "interceptorNames" >
(tng) (tng) (tng) (tng) (tng) (tng) <list>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <value>MyTransactionInterceptor</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <value>MyHibernateInterceptor</value>
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) <value>MyBusinessObjectTarget</value>
(tng) (tng) (tng) (tng) (tng) (tng) </list>
(tng) (tng) (tng) </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 (tng) net.sf.hibernate.*;
import (tng) org.springframework.orm.hibernate.SessionFactoryUtils;
....
public (tng) Book (tng)findBook( int (tng) bookID) (tng) throws (tng) BookNotFoundException, (tng)DataAccessException
{
(tng) (tng) (tng) //get (tng)the (tng)Session (tng)instance (tng)already (tng)bound (tng)to (tng)current (tng)thread (tng)and (tng)opened
(tng) (tng) (tng) Session (tng)session (tng)= (tng)SessionFactoryUtils.getSession(getSessionFactory(), (tng) false );
(tng) (tng) (tng) try
(tng) (tng) (tng) {
(tng) (tng) (tng) (tng) (tng) (tng) Book (tng)book (tng)= (tng)(Book)session.load(Book.class, (tng)bookID);
(tng) (tng) (tng) (tng) (tng) (tng) return (tng) book;
(tng) (tng) (tng) }
(tng) (tng) (tng) catch (ObjectNotFoundException (tng)e)
(tng) (tng) (tng) {
(tng) (tng) (tng) (tng) (tng) (tng) throw (tng)new (tng) BookNotFoundException();
(tng) (tng) (tng) }
(tng) (tng) (tng) catch (HibernateException (tng)e)
(tng) (tng) (tng) {
(tng) (tng) (tng) (tng) (tng) (tng) throw (tng) SessionFactoryUtils.convertHibernateAccessException(e);
(tng) (tng) (tng) }
}
(tng)
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?).
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"><value>messages</value></property>
</bean>
2 Bean
<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
<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>
Bean IntelliJ Live Template Bean
/*
* Created on 2004-8-25 by simba.
*
*/
package com.simba.blog.util;
(tng)
(tng)
import javax.servlet.ServletContext;
(tng)
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
(tng)
import com.opensymphony.webwork.ServletActionContext;
import com.simba.blog.service.BlogService;
(tng)
/**
* @author simba
*
* email: simbasun@msn.com
*/
public class ServiceLocator
{
//the catalog service bean name
private static final String BLOG_SERVICE_BEAN_NAME = "blogService";
(tng)
//the user service bean name
private static final String USER_SERVICE_BEAN_NAME = "userService";
(tng)
//the logger for this class
private Log logger = LogFactory.getLog(this.getClass());
(tng)
//the Spring application context
private ApplicationContext appContext;
(tng)
//the cached catalog service
private BlogService blogService;
(tng)
//the cached user service
//private UserService userService;
(tng)
/**
* 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");*/
(tng)
ServletContext context = ServletActionContext.getServletContext();
this.appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
this.blogService = (BlogService)this.lookupService(BLOG_SERVICE_BEAN_NAME);
(tng)
/*
* this.userService = (UserService)this.lookupService(USER_SERVICE_BEAN_NAME);
*/
(tng)
this.logger.info("Service locator bean is initialized");
}
(tng)
(tng)
/**
* 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);
}
(tng)
/**
* @return Returns the blogService.
*/
public BlogService getBlogService()
{
return blogService;
}
}
(tng)
(tng)
(tng)
[in web.xml]
(tng)
<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提供?jin)Hibernate的一层包?使Hibernate使用h更加方便,其是结合Hibernate Annotation? 配置文g更少,l护更加? 下面来看看吧.
下面是一个测试数据实体类TestData.java 和前一提到的实体cM?只不q用?jin)Annotation注释. (tng)
package (tng)
test.orm.hibernate.model;
import (tng)
javax.persistence.Basic;
import (tng)
javax.persistence.Entity;
import (tng)
javax.persistence.Id;
import (tng)
javax.persistence.Table;
@Entity
@Table(name (tng)= (tng)
"test"
,schema (tng)= (tng)
"APP"
)
public (tng)class (tng)
TestData (tng){
(tng) (tng)
private (tng)int (tng)
id;
(tng) (tng)
private (tng)
String (tng)name;
(tng) (tng)
public (tng)
TestData(
int (tng)
id, (tng)String (tng)name) (tng){
(tng) (tng) (tng) (tng)
this
.id (tng)= (tng)id;
(tng) (tng) (tng) (tng)
this
.name (tng)= (tng)name;
(tng) (tng)
}
(tng) (tng)
public (tng)
TestData() (tng){}
(tng) (tng)
(tng) (tng)
@Id
(tng) (tng)
public (tng)int (tng)
getId() (tng){
(tng) (tng) (tng) (tng)
return (tng)
id;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
setId(
int (tng)
id) (tng){
(tng) (tng) (tng) (tng)
this
.id (tng)= (tng)id;
(tng) (tng)
}
(tng) (tng)
@Basic
(tng) (tng)
public (tng)
String (tng)getName() (tng){
(tng) (tng) (tng) (tng)
return (tng)
name;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
setName(String (tng)name) (tng){
(tng) (tng) (tng) (tng)
this
.name (tng)= (tng)name;
(tng) (tng)
}
}
下面是测试Hibernate模板的类, TestHibernateTemplate.java
package (tng)
test.orm.hibernate;
import (tng)
java.sql.SQLException;
import (tng)
org.hibernate.HibernateException;
import (tng)
org.hibernate.Session;
import (tng)
org.springframework.orm.hibernate3.HibernateCallback;
import (tng)
org.springframework.orm.hibernate3.HibernateTemplate;
import (tng)
test.orm.hibernate.model.TestData;
public (tng)class (tng)
TestHibernateTemplate (tng){
(tng) (tng)
private (tng)
HibernateTemplate (tng)hibernateTemplate;
(tng) (tng)
(tng) (tng)
public (tng)
TestHibernateTemplate() (tng){}
(tng) (tng)
public (tng)
HibernateTemplate (tng)getHibernateTemplate() (tng){
(tng) (tng) (tng) (tng)
return (tng)
hibernateTemplate;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
setHibernateTemplate(HibernateTemplate (tng)hibernateTemplate) (tng){
(tng) (tng) (tng) (tng)
this
.hibernateTemplate (tng)= (tng)hibernateTemplate;
(tng) (tng)
}
(tng) (tng)
(tng) (tng)
//试使用HibernateTemplate来操作数?/font>
(tng) (tng)
public (tng)
TestData (tng)getTestData(
final (tng)int (tng)
id) (tng){
(tng) (tng) (tng) (tng)
return (tng)
(TestData) (tng)hibernateTemplate.execute(
new (tng)
HibernateCallback() (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
public (tng)
Object (tng)doInHibernate(Session (tng)s) (tng)
throws (tng)
HibernateException, (tng)SQLException (tng){
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
// (tng)TODO (tng)Auto-generated (tng)method (tng)stub
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
return (tng)
s.get(TestData.class, (tng)id);
(tng) (tng) (tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
});
(tng) (tng)
}
// (tng) (tng)上面查询数据的方法?tng)用?jin)HibernateCallBack接口,对于q样单的查询,可以使用下面由HibernateTemplate (tng)提供的更单的Ҏ(gu)
(tng) (tng)
public (tng)
TestData (tng)getTestData2(
final (tng)int (tng)
id) (tng){
(tng) (tng) (tng) (tng)
return (tng)
(TestData) (tng)hibernateTemplate.get(TestData.class, (tng)id);
(tng) (tng)
}
(tng) (tng)
/* (tng)如果上面的两处方法用Load (tng)Ҏ(gu)的时, (tng)抛出延迟加蝲属性异? (tng)Spring理Session的问? (tng)应该是Spring用完Session (tng)然后关闭了(jin),
(tng) (tng) (tng)
* (tng)所以不可以延迟加蝲, (tng)也就不可以用load (tng)Ҏ(gu)?,????? (tng)Z????*/
(tng) (tng)
(tng) (tng)
// (tng)其他操作数据Ҏ(gu) (tng)? (tng)插入,修改.... (tng)和用Hibernate (tng)session (tng)差不? (tng)HibernateTemplate只是Session的包?/font>
}
可以看到使用Hibernate模板来操作数?是多么简?
注意: 上面注释?提到?jin)在使用LoadҎ(gu)时??x)有问? Session in view 时候用load是不?x)出问题? q里Spring提供的方法应该是按照Hibernate的语义写的吧.
下面是用来试上面的类是否正常工作的测试类?TestApp.java
package (tng)
test.orm.hibernate;
import (tng)
org.springframework.context.ApplicationContext;
import (tng)
org.springframework.context.support.ClassPathXmlApplicationContext;
import (tng)
test.jdbc.DatabaseUtils;
import (tng)
test.orm.hibernate.model.TestData;
public (tng)class (tng)
TestApp (tng){
(tng) (tng)
/**
(tng) (tng) (tng)
* (tng)
@param (tng)
args
(tng) (tng) (tng)
*/
(tng) (tng)
public (tng)static (tng)void (tng)
main(String[] (tng)args) (tng){
(tng) (tng) (tng) (tng)
// (tng)TODO (tng)Auto-generated (tng)method (tng)stub
(tng) (tng) (tng) (tng)
DatabaseUtils (tng)dataUtils (tng)= (tng)
new (tng)
DatabaseUtils();
(tng) (tng) (tng) (tng)
dataUtils.connect();
(tng) (tng) (tng) (tng)
System.out.println(
"Open (tng)database:!"
);
(tng) (tng) (tng) (tng)
ApplicationContext (tng)context (tng)= (tng)
new (tng)
ClassPathXmlApplicationContext(
"test/orm/hibernate/spring-hibernate.xml"
);
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
TestHibernateTemplate (tng)hibernateTemplate (tng)= (tng)(TestHibernateTemplate) (tng)context.getBean(
"testDao"
);
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
System.out.println(
"name (tng): (tng)" (tng)
);
(tng) (tng) (tng) (tng)
TestData (tng)data (tng)= (tng)hibernateTemplate.getTestData(
9
);
(tng) (tng) (tng) (tng)
System.out.println(
"name (tng): (tng)" (tng)
+ (tng)data.getName());
(tng) (tng) (tng) (tng)
data (tng)= (tng)hibernateTemplate.getTestData2(
3
);
(tng) (tng) (tng) (tng)
System.out.println(
"name (tng)2: (tng)" (tng)
+ (tng)data.getName());
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
dataUtils.disconnect();
(tng) (tng) (tng) (tng)
(tng) (tng)
}
}
注意: 本测试中用到?jin)上一用到?font color="#000000">DatabaseUtils.java 工具cL操作Derby数据?
?/span>
以上实例可以看出,在Spring中用Hibernate,效率更高?sh)? 你不q样认ؓ(f)?
同时
Spring也提供了(jin) JDO{其他一些数据操作框架的包装,使用上基本上 (tng)是一L(fng)q里׃在详qC(jin).
下一ơ我们来看看Spring的I18N处理,和自定义属性编辑器的实?
(tng) BY: icess Blog: http://blog.matrix.org.cn/page/icess (tng)
(tng) (tng) 在Spring中处理I18N问题和用Java里面的类基本上是一L(fng).使用org.springframework.context.support.ResourceBundleMessageSource
然后注入资源文g(一个名字ؓ(f)basename的属?,然后可以在Context中用资源文件了(jin), 如下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
*/
(tng) public static void main(String[] args) { (tng) (tng) (tng) // TODO Auto-generated method stub
(tng) (tng) (tng) ApplicationContext context =
new ClassPathXmlApplicationContext( "test/i18n/test.xml" );(tng) (tng) (tng) String text = context.getMessage(
"sex" , new Object[0], Locale. US );(tng) (tng) (tng) String textZH = context.getMessage(
"sex" , new Object[0], Locale. CHINA );(tng) (tng) (tng) System.
out .println(text + " 中文:" +textZH);(tng) }
}
很简?q样可以了(jin).
下面来看看Spring中的属性自定义~辑?q个和Hibernate中的自定义属性差不多 ? 例如下面我们要看C(jin)例子,映射一个电(sh)话号??font size="2">areaCode,prefix?number, 如果不用自定义属性编辑器那么p分别注入上面?个代?ȝ(ch). 如果使用自定义属性编辑器,直接注入一?分开的数字序列就可以??/p>
888-666-9999
.在下面的例子中的Contact.javacL个PhoneNumber属?里面保存?sh)(jin)上面?个代?两个cȝ代码如下:
package (tng)
test.propertyEditor;
public (tng)class (tng)
Contact (tng){
(tng) (tng)
private (tng)
PhoneNumber (tng)phoneNumber;
(tng) (tng)
private (tng)
String (tng)name;
(tng) (tng)
(tng) (tng)
public (tng)
Contact() (tng){}
(tng) (tng)
(tng) (tng)
public (tng)
String (tng)getName() (tng){
(tng) (tng) (tng) (tng)
return (tng)
name;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
setName(String (tng)name) (tng){
(tng) (tng) (tng) (tng)
this
.name (tng)= (tng)name;
(tng) (tng)
}
(tng) (tng)
public (tng)
PhoneNumber (tng)getPhoneNumber() (tng){
(tng) (tng) (tng) (tng)
return (tng)
phoneNumber;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
setPhoneNumber(PhoneNumber (tng)phoneNumber) (tng){
(tng) (tng) (tng) (tng)
this
.phoneNumber (tng)= (tng)phoneNumber;
(tng) (tng)
}
(tng) (tng)
}
PhoneNumber.java
package (tng)
test.propertyEditor;
public (tng)class (tng)
PhoneNumber (tng){
(tng) (tng)
private (tng)
String (tng)areaCode;
(tng) (tng)
private (tng)
String (tng)prefix;
(tng) (tng)
private (tng)
String (tng)number;
(tng) (tng)
public (tng)
PhoneNumber() (tng){
(tng) (tng) (tng) (tng)
(tng) (tng)
}
(tng) (tng)
public (tng)
PhoneNumber(String (tng)areaCode,String (tng)prefix,String (tng)number) (tng){
(tng) (tng) (tng) (tng)
this
.areaCode (tng)= (tng)areaCode;
(tng) (tng) (tng) (tng)
this
.prefix (tng)= (tng)prefix;
(tng) (tng) (tng) (tng)
this
.number (tng)= (tng)number;
(tng) (tng)
}
(tng) (tng)
public (tng)
String (tng)getAreaCode() (tng){
(tng) (tng) (tng) (tng)
return (tng)
areaCode;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
setAreaCode(String (tng)areaCode) (tng){
(tng) (tng) (tng) (tng)
this
.areaCode (tng)= (tng)areaCode;
(tng) (tng)
}
(tng) (tng)
public (tng)
String (tng)getNumber() (tng){
(tng) (tng) (tng) (tng)
return (tng)
number;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
setNumber(String (tng)number) (tng){
(tng) (tng) (tng) (tng)
this
.number (tng)= (tng)number;
(tng) (tng)
}
(tng) (tng)
public (tng)
String (tng)getPrefix() (tng){
(tng) (tng) (tng) (tng)
return (tng)
prefix;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
setPrefix(String (tng)prefix) (tng){
(tng) (tng) (tng) (tng)
this
.prefix (tng)= (tng)prefix;
(tng) (tng)
}
}
然后定义一个用来编辑PhoneNumber的编辑器PhoneEditor.java 如下:
package (tng)
test.propertyEditor;
import (tng)
java.beans.PropertyEditorSupport;
public (tng)class (tng)
PhoneEditor (tng)
extends (tng)
PropertyEditorSupport (tng){
(tng) (tng)
public (tng)void (tng)
setAsText(String (tng)textValue) (tng){
(tng) (tng) (tng) (tng)
String (tng)stripped (tng)= (tng)stripNonNumber(textValue);
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
String (tng)areaCode (tng)= (tng)stripped.substring(
0
,
3
);
(tng) (tng) (tng) (tng)
String (tng)prefix (tng)= (tng)stripped.substring(
3
,
6
);
(tng) (tng) (tng) (tng)
String (tng)number (tng)= (tng)stripped.substring(
6
);
(tng) (tng) (tng) (tng)
PhoneNumber (tng)phone (tng)= (tng)
new (tng)
PhoneNumber(areaCode,prefix,number);
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
setValue(phone);
(tng) (tng)
}
(tng) (tng)
(tng) (tng)
private (tng)
String (tng)stripNonNumber(String (tng)original) (tng){
(tng) (tng) (tng) (tng)
StringBuilder (tng)allNumeric (tng)= (tng)
new (tng)
StringBuilder();
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
for
(
int (tng)
i (tng)= (tng)
0
; (tng)i (tng)< (tng)original.length(); (tng)i (tng)++) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
char (tng)
c (tng)= (tng)original.charAt(i);
(tng) (tng) (tng) (tng) (tng) (tng)
if
(Character.isDigit(c)) (tng){
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
allNumeric.append(c);
(tng) (tng) (tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng)
return (tng)
allNumeric.toString();
(tng) (tng)
}
}
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 (tng)
test.propertyEditor;
import (tng)
org.springframework.context.ApplicationContext;
import (tng)
org.springframework.context.support.ClassPathXmlApplicationContext;
public (tng)class (tng)
TestPropertyEditor (tng){
(tng) (tng)
/**
(tng) (tng) (tng)
* (tng)
@param (tng)
args
(tng) (tng) (tng)
*/
(tng) (tng)
public (tng)static (tng)void (tng)
main(String[] (tng)args) (tng){
(tng) (tng) (tng) (tng)
// (tng)TODO (tng)Auto-generated (tng)method (tng)stub
(tng) (tng) (tng) (tng)
ApplicationContext (tng)context (tng)= (tng)
new (tng)
ClassPathXmlApplicationContext(
"test/propertyEditor/testPropertyEditor.xml"
);
(tng) (tng) (tng) (tng)
Contact (tng)c (tng)= (tng)(Contact) (tng)context.getBean(
"contact"
);
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
System.out.println(c.getPhoneNumber().getAreaCode());
(tng) (tng) (tng) (tng)
System.out.println(c.getPhoneNumber().getPrefix());
(tng) (tng) (tng) (tng)
System.out.println(c.getPhoneNumber().getNumber());
(tng) (tng)
}
}
ok, 很简?下一ơ来看看,Spring提供的一下比较有意思的功能.如定?发送Email{?
Spring in Action
W记
(II)
今天来看看用JDBC来操作数据:(x) 使用的是DerbyQJavaDBQ数据库Q关于JavaDB的介l请点击q里Q?/font>
http://blog.matrix.org.cn/page/icess?catname=%2FJavaDB
?下面建立一个DatabaseUtils.java的工L(fng)Q来操作数据?。该cd上面的连接的文章中有讲述?/font>
package (tng)
test.jdbc;
import (tng)
java.io.File;
import (tng)
java.io.IOException;
import (tng)
java.io.InputStream;
import (tng)
java.sql.Connection;
import (tng)
java.sql.DriverManager;
import (tng)
java.sql.PreparedStatement;
import (tng)
java.sql.ResultSet;
import (tng)
java.sql.SQLException;
import (tng)
java.sql.Statement;
import (tng)
java.util.Properties;
import (tng)
java.util.logging.Logger;
public (tng)class (tng)
DatabaseUtils (tng){
(tng) (tng)
private (tng)static (tng)final (tng)
String (tng)DB_PROPERTIES_FILE (tng)= (tng)
"jdbc.properties"
;
(tng) (tng)
private (tng)static (tng)final (tng)
String (tng)DB_OPPOSITE_LOCATION (tng)= (tng)
"/.test"
;
(tng) (tng)
static (tng)
Logger (tng)logger (tng)= (tng)Logger.getLogger(DatabaseUtils.
class
.getName());
(tng) (tng)
private (tng)
Connection (tng)dbConnection;
(tng) (tng)
private (tng)
Properties (tng)dbProperties;
(tng) (tng)
private (tng)boolean (tng)
isConnected;
(tng) (tng)
// (tng)database (tng)name
(tng) (tng)
private (tng)
String (tng)dbName;
(tng) (tng)
private (tng)static (tng)final (tng)
String (tng)strCreateTestClobTeble (tng)= (tng)
"CREATE (tng)TABLE (tng)APP.test (tng)(id (tng)I(yng)NT, (tng)name (tng)VARCHAR(30),text (tng)CLOB(64 (tng)K))"
;
(tng) (tng)
private (tng)static (tng)final (tng)
String (tng)strInsertIntoTestTeble (tng)= (tng)
"INSERT (tng)I(yng)NTO (tng)APP.test (tng)(id, (tng)name) (tng) (tng)VALUES (tng)(?, (tng)?)"
;
(tng) (tng)
public (tng)static (tng)final (tng)
String (tng)strGetTest (tng)= (tng)
"SELECT (tng)* (tng)FROM (tng)APP.test (tng)WHERE (tng)I(yng)D (tng)= (tng)?"
;
(tng) (tng)
private (tng)static (tng)final (tng)
String (tng)strCreateCourseTable (tng)= (tng)
"create (tng)table (tng)APP.Course (tng)("
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)I(yng)D (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)I(yng)NTEGER (tng)NOT (tng)NULL (tng)PRIMARY (tng)KEY (tng)GENERATED (tng)ALWAYS (tng)AS (tng)I(yng)DENTITY (tng)(START (tng)WITH (tng)1, (tng)I(yng)NCREMENT (tng)BY (tng)1),"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)name (tng) (tng) (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)description (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)startDate (tng) (tng)DATE, (tng)" (tng)
+ (tng)
" (tng) (tng) (tng) (tng)endDate (tng) (tng) (tng) (tng) (tng)DATE (tng)" (tng)
+ (tng)
")"
;
(tng) (tng)
private (tng)static (tng)final (tng)
String (tng)strCreateStudentTable (tng)= (tng)
"create (tng)table (tng)APP.ADDRESS (tng)("
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)I(yng)D (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)I(yng)NTEGER (tng)NOT (tng)NULL (tng)PRIMARY (tng)KEY (tng)GENERATED (tng)ALWAYS (tng)AS (tng)I(yng)DENTITY (tng)(START (tng)WITH (tng)1, (tng)I(yng)NCREMENT (tng)BY (tng)1),"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)LASTNAME (tng) (tng) (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)FIRSTNAME (tng) (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)MIDDLENAME (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)PHONE (tng) (tng) (tng) (tng) (tng) (tng) (tng)VARCHAR(20), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)EMAIL (tng) (tng) (tng) (tng) (tng) (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)ADDRESS1 (tng) (tng) (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)ADDRESS2 (tng) (tng) (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)CITY (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)VARCHAR(30), (tng)"
(tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)
" (tng) (tng) (tng) (tng)STATE (tng) (tng) (tng) (tng) (tng) (tng) (tng)VARCHAR(30), (tng)" (tng)
+ (tng)
")"
;
(tng) (tng)
public (tng)
DatabaseUtils() (tng){
(tng) (tng) (tng) (tng)
this
(
"test"
);
(tng) (tng)
}
(tng) (tng)
public (tng)
DatabaseUtils(String (tng)dbName) (tng){
(tng) (tng) (tng) (tng)
this
.dbName (tng)= (tng)dbName;
(tng) (tng) (tng) (tng)
setDBSystemDir();
(tng) (tng) (tng) (tng)
dbProperties (tng)= (tng)loadDBProperties();
(tng) (tng) (tng) (tng)
String (tng)driverName (tng)= (tng)dbProperties.getProperty(
"db.driver"
);
(tng) (tng) (tng) (tng)
loadDatabaseDriver(driverName);
(tng) (tng) (tng) (tng)
if (tng)
(!dbExists()) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
createDatabase();
(tng) (tng) (tng) (tng)
}
(tng) (tng)
}
(tng) (tng)
private (tng)
Properties (tng)loadDBProperties() (tng){
(tng) (tng) (tng) (tng)
InputStream (tng)dbPropInputStream (tng)= (tng)
null
;
(tng) (tng) (tng) (tng)
dbPropInputStream (tng)= (tng)DatabaseUtils.
class
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
.getResourceAsStream(DB_PROPERTIES_FILE);
(tng) (tng) (tng) (tng)
dbProperties (tng)= (tng)
new (tng)
Properties();
(tng) (tng) (tng) (tng)
try (tng)
{
(tng) (tng) (tng) (tng) (tng) (tng)
dbProperties.load(dbPropInputStream);
(tng) (tng) (tng) (tng)
} (tng)
catch (tng)
(IOException (tng)e) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
e.printStackTrace();
(tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng)
return (tng)
dbProperties;
(tng) (tng)
}
(tng) (tng)
private (tng)void (tng)
setDBSystemDir() (tng){
(tng) (tng) (tng) (tng)
String (tng)userDir (tng)= (tng)System.getProperty(
"user.dir"
, (tng)
"."
);
(tng) (tng) (tng) (tng)
String (tng)systemDir (tng)= (tng)userDir (tng)+ (tng)DB_OPPOSITE_LOCATION;
(tng) (tng) (tng) (tng)
System.setProperty(
"derby.system.home"
, (tng)systemDir);
(tng) (tng) (tng) (tng)
// (tng)create (tng)the (tng)db (tng)System (tng)dir
(tng) (tng) (tng) (tng)
File (tng)fileSystemDir (tng)= (tng)
new (tng)
File(systemDir);
(tng) (tng) (tng) (tng)
fileSystemDir.mkdir();
(tng) (tng)
}
(tng) (tng)
private (tng)void (tng)
loadDatabaseDriver(String (tng)driverName) (tng){
(tng) (tng) (tng) (tng)
try (tng)
{
(tng) (tng) (tng) (tng) (tng) (tng)
Class.forName(driverName);
(tng) (tng) (tng) (tng)
} (tng)
catch (tng)
(ClassNotFoundException (tng)e) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
e.printStackTrace();
(tng) (tng) (tng) (tng)
}
(tng) (tng)
}
(tng) (tng)
private (tng)boolean (tng)
dbExists() (tng){
(tng) (tng) (tng) (tng)
boolean (tng)
bExists (tng)= (tng)
false
;
(tng) (tng) (tng) (tng)
String (tng)dbLocation (tng)= (tng)getDatabaseLocation();
(tng) (tng) (tng) (tng)
File (tng)dbFileDir (tng)= (tng)
new (tng)
File(dbLocation);
(tng) (tng) (tng) (tng)
if (tng)
(dbFileDir.exists()) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
bExists (tng)= (tng)
true
;
(tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng)
return (tng)
bExists;
(tng) (tng)
}
(tng) (tng)
private (tng)boolean (tng)
createDatabase() (tng){
(tng) (tng) (tng) (tng)
boolean (tng)
bCreated (tng)= (tng)
false
;
(tng) (tng) (tng) (tng)
Connection (tng)dbConnection (tng)= (tng)
null
;
(tng) (tng) (tng) (tng)
String (tng)dbUrl (tng)= (tng)getDatabaseUrl();
(tng) (tng) (tng) (tng)
dbProperties.put(
"create"
, (tng)
"true"
);
(tng) (tng) (tng) (tng)
try (tng)
{
(tng) (tng) (tng) (tng) (tng) (tng)
dbConnection (tng)= (tng)DriverManager.getConnection(dbUrl, (tng)dbProperties);
(tng) (tng) (tng) (tng) (tng) (tng)
bCreated (tng)= (tng)createTables(dbConnection, (tng)strCreateTestClobTeble);
(tng) (tng) (tng) (tng)
} (tng)
catch (tng)
(SQLException (tng)e) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
e.printStackTrace();
(tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng)
dbProperties.remove(
"create"
);
(tng) (tng) (tng) (tng)
return (tng)
bCreated;
(tng) (tng)
}
(tng) (tng)
private (tng)boolean (tng)
createTables(Connection (tng)dbConnection, (tng)String (tng)creatTableSql) (tng){
(tng) (tng) (tng) (tng)
boolean (tng)
bCreatedTables (tng)= (tng)
false
;
(tng) (tng) (tng) (tng)
Statement (tng)statement (tng)= (tng)
null
;
(tng) (tng) (tng) (tng)
try (tng)
{
(tng) (tng) (tng) (tng) (tng) (tng)
statement (tng)= (tng)dbConnection.createStatement();
(tng) (tng) (tng) (tng) (tng) (tng)
statement.execute(creatTableSql);
(tng) (tng) (tng) (tng) (tng) (tng)
bCreatedTables (tng)= (tng)
true
;
(tng) (tng) (tng) (tng)
} (tng)
catch (tng)
(SQLException (tng)e) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
e.printStackTrace();
(tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng)
return (tng)
bCreatedTables;
(tng) (tng)
}
(tng) (tng)
public (tng)
String (tng)getDatabaseUrl() (tng){
(tng) (tng) (tng) (tng)
return (tng)
dbProperties.getProperty(
"db.url"
) (tng)+ (tng)dbName;
(tng) (tng)
}
(tng) (tng)
public (tng)
String (tng)getDatabaseLocation() (tng){
(tng) (tng) (tng) (tng)
String (tng)dbLocation (tng)= (tng)System.getProperty(
"derby.system.home"
) (tng)+ (tng)
"/"
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
+ (tng)dbName;
(tng) (tng) (tng) (tng)
return (tng)
dbLocation;
(tng) (tng)
}
(tng) (tng)
public (tng)boolean (tng)
connect() (tng){
(tng) (tng) (tng) (tng)
String (tng)dbUrl (tng)= (tng)getDatabaseUrl();
(tng) (tng) (tng) (tng)
try (tng)
{
(tng) (tng) (tng) (tng) (tng) (tng)
logger.info(
"DBUrl: (tng)" (tng)
+ (tng)dbUrl);
(tng) (tng) (tng) (tng) (tng) (tng)
dbConnection (tng)= (tng)DriverManager.getConnection(dbUrl, (tng)dbProperties);
(tng) (tng) (tng) (tng) (tng) (tng)
isConnected (tng)= (tng)dbConnection (tng)!= (tng)
null
;
(tng) (tng) (tng) (tng)
} (tng)
catch (tng)
(SQLException (tng)e) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
// (tng)TODO (tng)Auto-generated (tng)catch (tng)block
(tng) (tng) (tng) (tng) (tng) (tng)
e.printStackTrace();
(tng) (tng) (tng) (tng) (tng) (tng)
isConnected (tng)= (tng)
false
;
(tng) (tng) (tng) (tng) (tng) (tng)
logger.info(
"create (tng)connection (tng)if (tng)failed!"
);
(tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng)
return (tng)
isConnected;
(tng) (tng)
}
(tng) (tng)
public (tng)
Connection (tng)getConnection() (tng){
(tng) (tng) (tng) (tng)
return (tng)
dbConnection;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
disconnect() (tng){
(tng) (tng) (tng) (tng)
if (tng)
(isConnected) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
String (tng)dbUrl (tng)= (tng)getDatabaseUrl();
(tng) (tng) (tng) (tng) (tng) (tng)
dbProperties.put(
"shutdown"
, (tng)
"true"
);
(tng) (tng) (tng) (tng) (tng) (tng)
try (tng)
{
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
System.out.println(
"断开数据库连????????????????"
);
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
DriverManager.getConnection(dbUrl, (tng)dbProperties);
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
System.out.println(
"????????????????"
);
(tng) (tng) (tng) (tng) (tng) (tng)
} (tng)
catch (tng)
(SQLException (tng)e) (tng){
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
// (tng)e.printStackTrace();
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
logger.info(
"disconnect (tng)the (tng)connection (tng)Successful!"
);
(tng) (tng) (tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng) (tng) (tng)
isConnected (tng)= (tng)
false
;
(tng) (tng) (tng) (tng)
}
(tng) (tng)
}
(tng) (tng)
/**
(tng) (tng) (tng)
* (tng)
@param (tng)
args
(tng) (tng) (tng)
*/
(tng) (tng)
public (tng)static (tng)void (tng)
main(String[] (tng)args) (tng){
(tng) (tng) (tng) (tng)
// (tng)TODO (tng)Auto-generated (tng)method (tng)stub
(tng) (tng) (tng) (tng)
DatabaseUtils (tng)testdb (tng)= (tng)
new (tng)
DatabaseUtils();
(tng) (tng) (tng) (tng)
logger.info(testdb.getDatabaseLocation());
(tng) (tng) (tng) (tng)
logger.info(testdb.getDatabaseUrl());
(tng) (tng) (tng) (tng)
testdb.connect();
(tng) (tng) (tng) (tng)
Connection (tng)c (tng)= (tng)testdb.getConnection();
(tng) (tng) (tng) (tng)
PreparedStatement (tng)ps (tng)= (tng)
null
;
(tng) (tng) (tng) (tng)
try (tng)
{
(tng) (tng) (tng) (tng) (tng) (tng)
ps (tng)= (tng)c.prepareStatement(DatabaseUtils.strInsertIntoTestTeble, (tng)Statement.RETURN_GENERATED_KEYS);
(tng) (tng) (tng) (tng) (tng) (tng)
ps.setInt(
1
, (tng)
1
);
(tng) (tng) (tng) (tng) (tng) (tng)
ps.setString(
2
, (tng)
"test (tng)I(yng)cerain"
);
(tng) (tng) (tng) (tng) (tng) (tng)
int (tng)
i (tng)=ps.executeUpdate();
(tng) (tng) (tng) (tng) (tng) (tng)
System.out.println(i);
(tng) (tng) (tng) (tng) (tng) (tng)
ps.close();
(tng) (tng) (tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng) (tng) (tng)
ps (tng)= (tng)c.prepareStatement(DatabaseUtils.strGetTest);
(tng) (tng) (tng) (tng) (tng) (tng)
ps.setInt(
1
, (tng)
1
);
(tng) (tng) (tng) (tng) (tng) (tng)
ResultSet (tng)rs (tng)= (tng)ps.executeQuery();
(tng) (tng) (tng) (tng) (tng) (tng)
if
(rs.next()) (tng){
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
String (tng)name (tng)= (tng)rs.getString(
2
);
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
System.out.println(name);
(tng) (tng) (tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng) (tng) (tng)
ps.close();
(tng) (tng) (tng) (tng)
} (tng)
catch (tng)
(SQLException (tng)e) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
// (tng)TODO (tng)Auto-generated (tng)catch (tng)block
(tng) (tng) (tng) (tng) (tng) (tng)
e.printStackTrace();
(tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng)
testdb.disconnect();
(tng) (tng)
}
}
下面是一个插入数据的c?InsertData.java
package (tng)
test.jdbc;
import (tng)
java.sql.Types;
import (tng)
javax.sql.DataSource;
import (tng)
org.springframework.jdbc.core.SqlParameter;
import (tng)
org.springframework.jdbc.object.SqlUpdate;
public (tng)class (tng)
InsertData (tng)
extends (tng)
SqlUpdate (tng){
(tng) (tng)
// (tng)需要注入一个DataSource...
(tng) (tng)
public (tng)
InsertData(DataSource (tng)ds) (tng){
(tng) (tng) (tng) (tng)
setDataSource(ds); (tng) (tng)
// (tng)TODO (tng)注意 (tng)讄数据?/font>
(tng) (tng) (tng) (tng)
setSql(
"INSERT (tng)I(yng)NTO (tng)APP.test (tng)(id, (tng)name) (tng) (tng)VALUES (tng)(?, (tng)?)"
);
(tng) (tng) (tng) (tng)
declareParameter(
new (tng)
SqlParameter(Types.INTEGER));
(tng) (tng) (tng) (tng)
declareParameter(
new (tng)
SqlParameter(Types.VARCHAR));
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
compile();
// (tng)TODO (tng)注意 (tng), (tng)要编译以后才可以使用
(tng) (tng)
}
(tng) (tng)
(tng) (tng)
// (tng)覆盖insertҎ(gu)
(tng) (tng)
public (tng)int (tng)
insert(TestData (tng)data) (tng){
(tng) (tng) (tng) (tng)
Object[] (tng)params (tng)= (tng)
new (tng)
Object[] (tng){data.id,data.name};
(tng) (tng) (tng) (tng)
return (tng)
update(params); (tng) (tng)
// (tng)执行插入操作....
(tng) (tng)
}
}
很简? q带有详l注?
下面是一个查询的c?QueryDataById.java
package (tng)
test.jdbc;
import (tng)
java.sql.ResultSet;
import (tng)
java.sql.SQLException;
import (tng)
java.sql.Types;
import (tng)
javax.sql.DataSource;
import (tng)
org.springframework.jdbc.core.SqlParameter;
import (tng)
org.springframework.jdbc.object.MappingSqlQuery;
public (tng)class (tng)
QueryDataById (tng)
extends (tng)
MappingSqlQuery{
(tng) (tng)
private (tng)static (tng)final (tng)
String (tng)sql (tng)= (tng)
"SELECT (tng)* (tng)FROM (tng)APP.test (tng)WHERE (tng)I(yng)D (tng)= (tng)?"
;
(tng) (tng)
public (tng)
QueryDataById(DataSource (tng)ds) (tng){
(tng) (tng) (tng) (tng)
super
(ds,sql);
(tng) (tng) (tng) (tng)
declareParameter(
new (tng)
SqlParameter(
"id"
,Types.INTEGER));
(tng) (tng) (tng) (tng)
compile();
(tng) (tng)
}
(tng)
// (tng)覆盖mapRowҎ(gu)
(tng) (tng)
@Override
(tng) (tng)
protected (tng)
Object (tng)mapRow(ResultSet (tng)rs, (tng)
int (tng)
index) (tng)
throws (tng)
SQLException (tng){
(tng) (tng) (tng) (tng)
// (tng)TODO (tng)Auto-generated (tng)method (tng)stub
(tng) (tng) (tng) (tng)
TestData (tng)tdata (tng)= (tng)
new (tng)
TestData();
(tng) (tng) (tng) (tng)
tdata.id (tng)= (tng)rs.getInt(
1
);
(tng) (tng) (tng) (tng)
tdata.name (tng)= (tng)rs.getString(
2
);
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
return (tng)
tdata;
(tng) (tng)
}
(tng) (tng)
}
也很?
注意:
(tng)以上两个c都实现?jin)Spring化Jdbc操作的一些接? 关于接口的信息请查考文? q里不在详细讲述.
下面是一个很单的试(数据)实体c?TestData.java
package (tng)
test.jdbc;
public (tng)class (tng)
TestData (tng){
(tng) (tng)
public (tng)int (tng)
id;
(tng) (tng)
public (tng)
String (tng)name;
(tng) (tng)
public (tng)
TestData(
int (tng)
id, (tng)String (tng)name) (tng){
(tng) (tng) (tng) (tng)
this
.id (tng)= (tng)id;
(tng) (tng) (tng) (tng)
this
.name (tng)= (tng)name;
(tng) (tng)
}
(tng) (tng)
public (tng)
TestData() (tng){}
}
下面是一个测试数据源是否注入正确的类:TestDataSource.java
package (tng)
test.jdbc;
import (tng)
java.sql.Connection;
import (tng)
java.sql.PreparedStatement;
import (tng)
java.sql.ResultSet;
import (tng)
javax.sql.DataSource;
public (tng)class (tng)
TestDataSource (tng){
(tng) (tng)
private (tng)
DataSource (tng)dataSource;
// 注入数据?/span>
(tng) (tng)
public (tng)void (tng)
setDataSource(DataSource (tng)dataSource) (tng){
(tng) (tng) (tng) (tng)
this
.dataSource (tng)= (tng)dataSource;
(tng) (tng)
}
//试数据?/span>
(tng) (tng)
public (tng)void (tng)
testDataSource() (tng){
(tng) (tng) (tng) (tng)
try (tng)
{
(tng) (tng) (tng) (tng) (tng) (tng)
System.out.println(
"Test (tng)DataSource!!!"
);
(tng) (tng) (tng) (tng) (tng) (tng)
Connection (tng)connection (tng)= (tng)dataSource.getConnection();
(tng) (tng) (tng) (tng) (tng) (tng)
if (tng)
(connection (tng)!= (tng)
null
)
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
System.out.println(
"test (tng)ok!"
);
(tng) (tng) (tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng) (tng) (tng)
PreparedStatement (tng)ps (tng)= (tng)
null
;
(tng) (tng) (tng) (tng) (tng) (tng)
ps (tng)= (tng)connection.prepareStatement(DatabaseUtils.strGetTest);
(tng) (tng) (tng) (tng) (tng) (tng)
ps.setInt(
1
, (tng)
1
);
(tng) (tng) (tng) (tng) (tng) (tng)
ResultSet (tng)rs (tng)= (tng)ps.executeQuery();
(tng) (tng) (tng) (tng) (tng) (tng)
if
(rs.next()) (tng){
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
String (tng)name (tng)= (tng)rs.getString(
2
);
(tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)
System.out.println(
"试数据源配|?" (tng)
+ (tng)name);
(tng) (tng) (tng) (tng) (tng) (tng)
}
(tng) (tng) (tng) (tng) (tng) (tng)
ps.close();
(tng) (tng) (tng) (tng)
} (tng)
catch (tng)
(Exception (tng)e) (tng){
(tng) (tng) (tng) (tng) (tng) (tng)
e.printStackTrace();
(tng) (tng) (tng) (tng)
}
(tng) (tng)
}
}
下面是测试Spring提高的Jdbc功能的主要测试类, 试?jin)一些用JDBC操作数据的常用功? 其他没有试的请查看其Doc,TestJdbcTemplate.java
整理一下我目前可能?x)用到的模? 对于那些现在Ҏ(gu)用不到的冬冬q是{有旉再研I吧!
W一个当然是最l典的HelloWorld ?, 呵呵,?但是说明?jin)原?
定义一个服务接?/p>
package (tng)
test.helloworld;
public (tng)interface (tng)
GreetingService (tng){
(tng) (tng)
public (tng)void (tng)
sayGreeting();
}
下面是其实现:
package (tng)
test.helloworld;
public (tng)class (tng)
GreetingServiceImpl (tng)
implements (tng)
GreetingService (tng){
(tng) (tng)
private (tng)
String (tng)greeting;
(tng) (tng)
public (tng)
GreetingServiceImpl() (tng){}
(tng) (tng)
public (tng)
GreetingServiceImpl(String (tng)greeting) (tng){
(tng) (tng) (tng) (tng)
this
.greeting (tng)= (tng)greeting;
(tng) (tng)
}
(tng) (tng)
public (tng)void (tng)
sayGreeting() (tng){
(tng) (tng) (tng) (tng)
// (tng) (tng)Auto-generated (tng)method (tng)stub
(tng) (tng) (tng) (tng)
System.out.println(greeting);
(tng) (tng)
}
(tng) (tng)
(tng) (tng)
public (tng)void (tng)
setGreeting(String (tng)greeting) (tng){
(tng) (tng) (tng) (tng)
this
.greeting (tng)= (tng)greeting;
(tng) (tng)
}
}
然后是试 IoC 的测试代?
package (tng)
test.helloworld;
import (tng)
org.springframework.beans.factory.BeanFactory;
import (tng)
org.springframework.beans.factory.xml.XmlBeanFactory;
import (tng)
org.springframework.context.ApplicationContext;
import (tng)
org.springframework.context.support.ClassPathXmlApplicationContext;
import (tng)
org.springframework.core.io.FileSystemResource;
public (tng)class (tng)
HelloApp (tng){
(tng) (tng)
/**
(tng) (tng) (tng)
* (tng)
@param (tng)
args
(tng) (tng) (tng)
*/
(tng) (tng)
public (tng)static (tng)void (tng)
main(String[] (tng)args) (tng){
(tng) (tng) (tng) (tng)
// (tng)TODO (tng)Auto-generated (tng)method (tng)stub
// (tng) (tng) (tng) (tng)BeanFactory (tng)factory;
// (tng) (tng) (tng) (tng)factory (tng)= (tng)new (tng)XmlBeanFactory(new (tng)FileSystemResource("src/test/helloworld/hello.xml"));
(tng) (tng) (tng) (tng)
// (tng)使用不同的方法得到bean. (tng)(BeanFactory (tng)or (tng)ApplicationContext)
(tng) (tng) (tng) (tng)
ApplicationContext (tng)context (tng)= (tng)
new (tng)
ClassPathXmlApplicationContext(
"test/helloworld/hello.xml"
);
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
(tng) (tng) (tng) (tng)
GreetingService (tng)greetingService (tng)= (tng)(GreetingService) (tng)context.getBean(
"greetingService"
);
// (tng) (tng) (tng) (tng)GreetingService (tng)greetingService (tng)= (tng)(GreetingService) (tng)factory.getBean("greetingService");
(tng) (tng) (tng) (tng)
greetingService.sayGreeting();
(tng) (tng)
}
}
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的内?在这里不作讨?因ؓ(f)现在我用AoP的地方不是很?单的应用是很单的?^_^.
下面一我们来看看 在spring包装下的jdbc讉K. 详细情况也可?b>参考这?/a>
回顾Q?/strong>
开源框架在以前的项目中用过不少Q?struts,webwork,hibernate,前两者属于web框架Q后者属于ORM框架Q这些框架基本都是侧重于应用的某个层面,不能UC为J2EE全面的框Ӟ往往是需要和其他框架相结合,我开发的目采用q如下组合,有的Ҏ(gu)没有框Ӟ只是自己做了(jin)设计装
a、servlet+jdbc,
b、servlet+jsp+javabean+jdbc,
c、struts+BD+DAO ,
d、webwork+ejb+hibernate
使用体会(x)Q?/strong>
(tng)a、b׃要说?jin),那时候的代码l构真是天马行空Q我当时初学Q维护公怸个项目代码,1个jsp实现一个模块所有功能,我花?jin)好长时间才?tng)L代码?br /> (tng) 时代L向前发展的,慢慢的系l结构层ơ上是越来越清晰Q开发效率也来高Q维护也来容易(Z一定的培训成本Q?br /> (tng)
(tng)相对而言Z设计理念来讲Q个人更喜欢webwork+hibernate框架l合?/font> (tng)
(tng)常有论同{开发层面框架的优缺点,我所属品的另外一个项目团队曾lؓ(f)目后箋(hu)开?应该采用strutsq是webwork争论的不可开交, (tng)其实个h认ؓ(f)struts,webwork都是非常好的web框架Q都有自w的优缺点:(x)
(tng)struts开创web MVC框架之先治I2003Q?004q开发的目基本都是采用struts框架Q当时招聘(sh)h的时候常问会(x)不会(x)strutsQ会(x)基本招?jin) (tng)?j)Q实际MVC设计理念很早提Z(jin)Q在j2SE中有很多使用之处Q但当时没有一个web框架开发中很好的诏彻该设计理念Q直到struts (tng)出现Q可能有Q只是没有apache更引人注意)(j)?br />
(tng)struts优点使用用户多,相关技术文比较全面,开发遇到的问题相关案例也比较多、比较容易解冻I框架更加E_(q一炚w帔R要)(j)?br /> (tng)
(tng)webwork相对于struts来讲Q的有不少优异之处Q(也是情理之中的事Q后发布的框架如果再没有优点别h也不?x)用?j)
(tng)个h认ؓ(f)在开发上主要有以下几点:(x)
(tng)1、页面数据封装成值对象功能比struts强大Qwebwork采用ognlcd转化?br /> (tng)struts只能对简单类型的数据对象以及(qing)文g对象装成值对? (tng)而webwork装的值对象基本没有限Ӟ值对象属性还可以是List,Mapq些对象Q这个特性在我们开发一些批量数据、复杂数据处理时非常方便Q,减少很多代码量,代码非常整洁?br /> (tng)
(tng)2、拦截器功能
(tng)拦截器是webwork的一个亮点,实际上也是业界比较流行的AOP的一个体玎ͼ在实际开发中你可以配|默认的拦截器, (tng)也可以ؓ(f)单独的模块指定特定的拦截器,q且可自定义拦截器,action执行前后拦截都可以。?br /> (tng)
(tng)3、单元测试比较方?br /> (tng)最早用struts框架开发测试时Q相关的单元试基本是不好做的,无法qweb环境Q测试都是只做集成测试、系l测试?br /> (tng)使用webwork可以单对action相关属性赋|可以相关的单元测试,当然前提是在对应action中没有引用web环境相关的对象。?br /> (tng)其实webwork框架核心(j)q是xwork框架Q最早框架用在C/Sl构下,webwork只是xwork的一个在web环境的实玎ͼ只是ActionContext (tng)上下文发生了(jin)变化Q所以说action能够做到qweb环境也是情理之中的?/p>
4、配|文?br />webwork配置文g可以采用引用、承其他配|文件方式,在团队开发一般都是分模块开发,q样比较方便Q配|管理更Ҏ(gu)Q不?x)冲H?br />
5、模板技术集?br /> (tng)我在实际应用目中是采用velocity模板做视囑ֱ玎ͼ因ؓ(f)在对应版本的webwork框架中和velociy集成的相当好Q比较方便,
(tng)比直接写jsp代码更整z、同时利用velocity模板Ҏ(gu)结合每个action的配|文Ӟ可实现比较通用的页面查询、录入等视图的展现?br /> (tng)而struts是没有相x(chng)杉K成,不过struts的tag相对而言比webwork的tag好用Qwebwork如果视图是jspcdQ相关的tag真的比较ȝ(ch)
(tng)虽然tag 库很丰富Q这也是我ؓ(f)什么用velocity做视囄原因 。?br /> (tng)
6?框架的效验功?br /> (tng)老实_(d)两者框架的效验功能都比较麻?ch),相对而言webwork更加ȝ(ch)点,配置较多Q验证接口实现太ȝ(ch)Q我实际目使用中还?br /> (tng)自定义了(jin)相关后台验证接口Q?要验证的相关action只要实现相关接口卛_Q相x(chng)截器负责拦截验证Q?br /> (tng)大部分的效验Ҏ(gu)配置数据以及(qing)html对象自定义属性通过javascript通用效验Q实际上现在ZXmlHttp的ajax技术应用成熟的?br /> (tng)后台验证接口的用途会(x)逐渐淡化?
(tng)
(tng)说明上述比较版本?struts 1.0 /webwork 2.1.6Q后lstruts框架扩展很多功能Q不是很?jin)解Q可能和上诉描述不一定很d?br /> (tng)
(tng)现在两者已l合qӞ希望能结合两者的优势Q发展出更好web框架?br /> (tng)
7、关于Hibernateq是DAO模式Q个人徏议采用hibernate+DAO盔R合的模式Qhibernate占主导地位?br /> (tng)虽然DAO模式通过自动生成代码效率不会(x)低,但如果需求变更就比较ȝ(ch)Q维护修改代码较多,试工作量也较大Q?tng)但Hibernate不是万能的,在一些必要的应用q是采用DAO模式Q特别是性能相关的部分?
(tng)
(tng)
(tng)废话一大堆Q也把我的工作回顾?jin)一遍,呵呵Q这两天单了(jin)解Spring框架Q个人感觉Spring更像一个全面的J2EE框架解决Ҏ(gu), (tng)希望能够有时间系l的学习(fn)一把,大家有兴的请多多交?我会(x)我的学?fn)?j)得和大家分n?/p>