??xml version="1.0" encoding="utf-8" standalone="yes"?> 作?laoer 引自:http://bbscs.laoer.com/read.bbscs?bid=5&id=14419 全文如下:
可以看出Q有两种ҎQ一个是用ContextLoaderListenerq个ListernerQ另一个是ContextLoaderServletq个ServletQ这两个Ҏ都是在web应用启动的时候来初始化WebApplicationContextQ我个h认ؓListerner要比Servlet更好一些,因ؓListerner监听应用的启动和l束Q而Servlet得启动要E微延迟一些,如果在这时要做一些业务的操作Q启动的前后序是有影响的? 那么在ContextLoaderListener和ContextLoaderServlet中到底做了什么呢Q? 上面我们介绍了WebApplicationContext在Servlet容器中初始化的原理,一般的Web应用可以轻杄使用了,但是Q随着Struts的广泛应用,把Struts和Spring整个hQ是一个需要面对的问题QSpring本n也提供了Struts的相关类Q主要用的有org.springframework.web.struts.ActionSupportQ我们只要把自己的Actionl承自ActionSupportQ就是可以调用ActionSupport中getWebApplicationContext()的方法取出WebApplicationContextQ但q样一来在Action中,需要取得业务逻辑的地斚w要getBeanQ看上去不够z,所以Spring又提供了另一个方法,用org.springframework.web.struts.ContextLoaderPlugInQ这是一个Struts的PlugQ在Struts启动时加载,对于ActionQ可以像理Bean一h理Q在struts-config.xml中Action的配|变成类g面的样子 com.web.action.Aaction是Action的实现类QbusinessService是需要的业务逻辑QSpring会把businessService注入到Action中,在Action中只要写businessService的get和setҎ可以了Q还有一点,action的bean是singleton="false"Q即每次新徏一个实例,q也解决了Struts中Action的线E同步问题,具体q程是当用户做?aAction”的HTTPhQ当然应该是?aAction.do”)QStruts会找到这个Action的对应类org.springframework.web.struts.DelegatingActionProxyQDelegatingActionProxy是个代理c,它会Laction-servlet.xml文g中?aAction”对应的真正实现c,然后把它实例化,同时把需要的业务对象注入Q然后执行Action的executeҎ? 使用了ContextLoaderPlugInQ在struts-config.xml中变成类D样配|? 说到q里不知道大家会不会有这L问题Q如果用ContextLoaderPlugInQ如果我们有些程序是qStruts的Action环境Q我们怎么处理Q比如我们要自定义标记库Q在标记库中Q我们需要调用Spring理的业务层逻辑对象Q这时候我们就很麻烦,因ؓ只有在action中动态注入业务逻辑Q其他我们似乎不能取得Spring的WebApplicationContext? 别急,我们q是来看一下ContextLoaderPlugIn的源码(源码不再贴出Q,我们可以发现Q原来ContextLoaderPlugIn仍然是把WebApplicationContext攑֜ServletContext中,只是q个KEY不太一样了Q这个KEYgؓContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX+ModuleConfig.getPrefix()Q具体请查看源代码)Q这下好了,我们知道了WebApplicationContext攑֜哪里Q只要我们在Web应用中能够取到ServletContext也就能取到WebApplicationContext?) Spring是一个很强大的框Ӟ希望大家在用过E中不断的深入,了解其更多的Ҏ,我在q里抛砖引玉Q有什么不对的地方Q请大家指出? 本文是一关于DAO设计模式的入门文章,H出讲述了它的优点和不之处。另外,本文q介l了Spring 2.0 JDBC/DAO框架q示范了它如何妥善地解决传统DAO设计中的~陷?/p> 传统的DAO设计 数据讉K对象QDAOQ是一个集成层设计模式Q如Core J2EE Design Pattern 图书所归纳。它持久性存储访问和操作代码装C个单独的层中。本文的上下文中所提到的持久存储器是一个RDBMS. q一模式在业务逻辑层和持久存储层之间引入了一个抽象层Q如?所C。业务对象通过数据讉K对象来访问RDBMSQ数据源Q。抽象层改善了应用程序代码ƈ引入了灵zL。理ZQ当数据源改变时Q比如更换数据库供应商或是数据库的类型时Q仅需改变数据讉K对象Q从而把对业务对象的影响降到最低?/p> ?. 应用E序l构Q包括DAO之前和之后的部分 讲解了DAO设计模式的基知识Q下面将~写一些代码。下面的例子来自于一个公司域模型。简而言之,q家公司有几位员工工作在不同的部门,如销售部、市场部以及人力资源部。ؓ了简单v见,我们集中讨Z个称作“雇员”的实体?/p> 针对接口~程 DAO设计模式带来的灵zL首先要归功于一个对象设计的最佛_践:针对接口~程QP2IQ。这一原则规定实体必须实现一个供调用E序而不是实体自w用的接口。因此,可以L替换成不同的实现而对客户端代码只产生很小的媄响?/p> 我们据此用findBySalaryRangeQ)行ؓ定义Employee DAO接口QIEmployeeDAO.业务lg通过q个接口与DAO交互Q?/p> import java.util.Map; //Returns the list of employees who fall into the given salary public List findBySalaryRange(Map salaryMap); 提供DAO实现c?/strong> 接口已经定义Q现在必L供Employee DAO的具体实玎ͼEmployeeDAOImplQ?/p> import java.sql.Connection; public class EmployeeDAOImpl implements IEmployeeDAO{ public List findBySalaryRange(Map salaryMap) 它们装了所有与JDBC API的交互。如果用像Kodo或者Hibernate的O/R映射ҎQ则DAOcd以将q些产品的私有API打包?/p> 它们检索到的数据打包到一个与JDBC API无关的传输对象中Q然后将其返回给业务层作q一步处理?/p> 它们实质上是无状态的。唯一的目的是讉Kq更改业务对象的持久数据?/p> 在这个过E中Q它们像SQLException一h获Q何底层JDBC API或数据库报告的错误(例如Q数据库不可用、错误的SQL句法Q。DAO对象再次使用一个与JDBC无关的自定义q行时异常类DBExceptionQ通知业务对象q些错误?/p> 它们像Connection和PreparedStatement对象那样Q将数据库资源释攑֛池中Qƈ在用完ResultSet游标之后Q将其所占用的内存释放?/p> 因此QDAO层将底层的数据访问API抽象化,Z务层提供了一致的数据讉KAPI. 构徏DAO工厂 DAO工厂是典型的工厂设计模式实现Q用于ؓ业务对象创徏和提供具体的DAO实现。业务对象用DAO接口Q而不用了解实现类的具体情cDAO工厂带来的依赖反转(dependency inversionQ提供了极大的灵zL。只要DAO接口建立的约定未改变Q那么很Ҏ改变DAO实现Q例如,从straight JDBC实现到基于Kodo的O/R映射Q,同时又不影响客户的业务对象: 与业务组件的协作 现在该了解DAO怎样适应更复杂的情Ş。如前几节所qͼDAO与业务层lg协作获取和更Ҏ久业务数据。下面的清单展示了业务服务组件及其与DAO层的交互Q?/p> public class EmployeeBusinessServiceImpl implements public List getEmployeesWithinSalaryRange(Map salaryMap){ IEmployeeDAO empDAO = DAOFactory.getInstance() 交互q程十分z,完全不依赖于M持久性接口(包括JDBCQ?br /> DAO设计模式也有~点Q?/strong> 代码重复Q从EmployeeDAOImpl清单可以清楚地看刎ͼ对于ZJDBC的传l数据库讉KQ代码重复(如上面的_体字所C)是一个主要的问题。一遍又一遍地写着同样的代码,明显q背了基本的面向对象设计的代码重用原则。它对目成本、时间安排和工作产生明显的副面媄响?/p> 耦合QDAO代码与JDBC接口和核心collection耦合得非常紧密。从每个DAOcȝ导入声明的数量可以明昑֜看出q种耦合?/p> 资源耗损Q依据EmployeeDAOImplcȝ设计Q所有DAOҎ必须释放Ҏ获得的连接、声明、结果集{数据库资源的控制。这是危险的dQ因Z个编E新手可能很Ҏ漏掉那些U束。结果造成资源耗尽Q导致系l停机?/p> 错误处理QJDBC驱动E序通过抛出SQLException来报告所有的错误情况。SQLException是检查到的异常,所以开发h员被q去处理它,即不可能从q类D代码混ؕ的大多数异常中恢复过来。而且Q从SQLException对象获得的错误代码和消息特定于数据库厂商Q所以不可能写出可移植的DAO错误发送代码?/p> 脆弱的代码:在基于JDBC的DAO中,两个常用的Q务是讄声明对象的绑定变量和使用l果集检索数据。如果SQL where子句中的列数目或者位|更改了Q就不得不对代码执行更改、测试、重新部|这个严格的循环q程?/p> 让我们看看如何能够减这些问题ƈ保留DAO的大多数优点?/p> q入Spring DAO 先识别代码中发生变化的部分,然后这一部分代码分离出来或者封装v来,p解决以上所列出的问题。Spring的设计者们已经完全做到了这一点,他们发布了一个超U简z、健壮的、高度可伸羃的JDBC框架。固定部分(像检索连接、准备声明对象、执行查询和释放数据库资源)已经被一ơ性地写好Q所以该框架的一部分内容有助于消除在传统的基于JDBC的DAO中出现的~点?/p> ?昄的是Spring JDBC框架的主要组成部分。业务服务对象通过适当的接口l用DAO实现cRJdbcDaoSupport是JDBC数据讉K对象的超cR它与特定的数据源相兌。Spring Inversion of Control QIOCQ容器或BeanFactory负责获得相应数据源的配置详细信息Qƈ其与JdbcDaoSupport相关联。这个类最重要的功能就是子类可以使用JdbcTemplate对象?/p> ?. Spring JDBC框架的主要组?/p> JdbcTemplate是Spring JDBC框架中最重要的类。引用文献中的话Q“它化了JDBC的用,有助于避免常见的错误。它执行核心JDBC工作,保留应用代码以提供SQL和提取结果。”这个类通过执行下面的样板Q务来帮助分离JDBC DAO代码的静态部分: 从数据源索连接?/p> 准备合适的声明对象?/p> 执行SQL CRUD操作?/p> 遍历l果集,然后结果填入标准的collection对象?/p> 处理SQLException异常q将其{换成更加特定于错误的异常层次l构?/p> 利用Spring DAO重新~写 既然已基本理解了Spring JDBC框架Q现在要重新~写已有的代码。下面将逐步讲述如何解决前几节中提到的问题?/p> W一步:修改DAO实现c? 现在从JdbcDaoSupport扩展出EmployeeDAOImpl以获得JdbcTemplate. import org.springframework.jdbc.core.support.JdbcDaoSupport; public class EmployeeDAOImpl extends JdbcDaoSupport public List findBySalaryRange(Map salaryMap){ Double dblParams [] = {Double.valueOf((String) 在上面的清单中,传入参数映射中的值存储在双字节数l中Q顺序与SQL字符串中的位|参数相同。queryForListQ)Ҏ以包含MapQ用列名作ؓ键,一对应一列)的ListQ一对应一行)的方式返回查询结果。稍后我会说明如何返回传输对象列表?/p> 从简化的代码可以明显看出QJdbcTemplate鼓励重用Q这大大削减了DAO实现中的代码。JDBC和collection包之间的紧密耦合已经消除。由于JdbcTemplateҎ可确保在使用数据库资源后其按正的ơ序释放Q所以JDBC的资源耗损不再是一个问题?/p> 另外Q用Spring DAOӞ不必处理异常。JdbcTemplatecM处理SQLExceptionQƈҎSQL错误代码或错误状态将其{换成特定于Spring异常的层ơ结构。例如,试图向主键列插入重复值时Q将引发DataIntegrityViolationException.然而,如果无法从这一错误中恢复,无需处理该异常。因为Spring DAO的根异常cDataAccessException是运行时异常c,所以可以这样做。值得注意的是Spring DAO异常独立于数据访问实现。如果实现是由O/R映射解决Ҏ提供Q就会抛出同L异常?br /> h意P2I的灵zL;即极大地改动DAO实现Q业务服务实C只需量更改。这是由于业务服务现在由Spring容器q行理?/p> W三步:配置Bean Factory- Spring bean factory需要一个配|文件进行初始化q启动Spring框架。这个配|文件包含所有业务服务和带Spring bean容器的DAO实现cR除此之外,它还包含用于初始化数据源和JdbcDaoSupport的信息: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" <beans> <!-- Configure DAO --> <!-- Configure Business Service --> q个Spring bean容器通过调用JdbcDaoSupport提供的setDataSourceQ)ҎQ设|包含DAO实现的数据源对象?/p> W四步:试- 最后是~写JUnit试cR依照Spring的方式,需要在容器外部q行试。然而,从第三步中的配置文g可以清楚地看刎ͼ我们一直在使用WebLogic Serverq接池?/p> package com.bea.dev2dev.business; import java.util.*; public class EmployeeBusinessServiceImplTest extends TestCase { protected void setUp() throws Exception { /** 使用l定变量 到目前ؓ止,我们搜烦了工资介于最低值和最高g间的雇员。假讑֜某种情Ş下,业务用户惌颠倒这一范围。DAO代码很脆弱,不得不通过更改来满求的变化。这个问题在于用了静态的位置l定变量Q用“?”表C)。Spring DAO通过支持命名的绑定变量来挽救q个情况。修改的IEmployeeDAO清单引入了命名的l定变量Q用“:<some name>”表C)。注意查询中的变化,如下所C: import java.util.Map; //SQL String that will be executed //Returns the list of employees falling into the given salary range public List findBySalaryRange(Map salaryMap); 多数JDBC驱动E序仅支持位|绑定变量。所以,Spring DAO在运行时这个查询{换成位置l定、基于变量的查询Qƈ且设|正的l定变量。现在,Z完成q些dQ需要用NamedParameterJdbcDaoSupportcdNamedParameterJdbcTemplatec,以代替JdbcDaoSupport和JdbcTemplate.下面是修改后的DAO实现c: import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport; public class EmployeeDAOImpl extends NamedParameterJdbcDaoSupport public List findBySalaryRange(Map salaryMap){ NamedParameterJdbcTemplate tmplt = NamedParameterJdbcDaoSupport的getNamedParameterJdbcTemplateQ)Ҏq回一个NamedParameterJdbcTemplate实例Q该实例由数据源句柄q行了预初始化。Spring Beanfactory执行初始化Q务,从配|文件获得所有的详细信息。在执行Ӟ一旦将命名的参数替换成位置占位W,NamedParameterJdbcTemplate将操作委托lJdbcTemplate.可见Q用命名的参数使得DAOҎ不受底层SQL声明M更改的媄响?/p> 最后,如果数据库不支持自动cd转换Q需要如下所C,对JUnit试cM的initSalaryMapQ)ҎE做修改?/p> xQ已l说明ؓ了解决传lDAO设计中存在的问题Q如何封装和概括JdbcTemplatecMJDBC代码的静态部分。现在了解一下有兛_量的问题Q如讄l定变量、结果集遍历{。虽然Spring DAO已经拥有q些问题的一般化解决ҎQ但在某些基于SQL的情况下Q可能仍需要设|绑定变量?/p> 在尝试向Spring DAO转换的过E中Q介l了׃业务服务及其客户Z间的U定遭到破坏而导致的隐蔽q行旉误。这个错误的来源可以q溯到原始的DAO.dbcTemplate.queryForListQ)Ҏ不再q回EmployeeTO实例列表。而是q回一个map表(每个map是结果集的一行)?/p> 如您目前所知,JdbcTemplateZ模板Ҏ设计模式Q该模式利用JDBC API定义SQL执行工作。必L变这个工作流以修复被破坏的约定。第一个选择是在子类中更Ҏ扩展工作。您可以遍历JdbcTemplate.queryForListQ)q回的列表,用EmployeeTO实例替换map对象。然而,q会D我们一直竭力避免的静态代码与动态代码的混合。第二个选择是将代码插入JdbcTemplate提供的各U工作流修改钩子QhookQ。明智的做法是在一个不同的cM装传输对象填充代码Q然后通过钩子链接它。填充逻辑的Q何修改将不会改变DAO. ~写一个类Q其实现在Spring框架特定的接口中定义的方法,可以实现第二个选择。这些方法称为回调函敎ͼ通过JdbcTemplate向框架注册。当发生相应的事Ӟ例如Q遍历结果集q填充独立于框架的传输对象)Ӟ框架调用这些方法?/p> W一步:传输对象 下面是您可能感兴的传输对象。注意,以下所C的传输对象是固定的Q?/p> package com.bea.dev2dev.to; public final class EmployeeTO implements Serializable{ private int empNo; /** Creates a new instance of EmployeeTO */ W二步:实现回调接口 实现RowMapper接口Q填充来自结果集的传输对象。下面是一个例子: package com.bea.dev2dev.dao.mapper; import com.bea.dev2dev.to.EmployeeTO; public class EmployeeTOMapper implements RowMapper{ public Object mapRow(ResultSet rs, int rowNum) 注意实现cM应该Ҏ供的ResultSet对象调用nextQ)Ҏ。这由框架负责,该类只要从结果集的当前行提取值就行。回调实现抛出的MSQLException也由Spring框架处理?/p> W三步:插入回调接口 执行SQL查询ӞJdbcTemplate利用默认的RowMapper实现产生map列表。现在需要注册自定义回调实现来修改JdbcTemplate的这一行ؓ。注意现在用的是NamedParameterJdbcTemplate的queryQ)ҎQ而不是queryForListQ)ҎQ?/p> public class EmployeeDAOImpl extends NamedParameterJdbcDaoSupport public List findBySalaryRange(Map salaryMap){ NamedParameterJdbcTemplate daoTmplt = Spring DAO框架Ҏ行查询后q回的结果进行遍历。它在遍历的每一步调用EmployeeTOMappercd现的mapRowQ)ҎQ用EmployeeTO传输对象填充最l结果的每一行?/p> W四步:修改后的JUnitc?/strong> 现在要根据返回的传输对象试q些l果。ؓ此要Ҏ试方法进行修攏V?/p> public class EmployeeBusinessServiceImplTest extends TestCase { private IEmployeeBusinessService empBusiness; // all methods not shown in the listing remain the /** public void assertEquals(List expResult, List result){ 优势 Spring JDBC框架的优点很清楚。我们获益很多,q将DAOҎ化到只有几行代码。代码不再脆弱,q要感谢该框架对命名的参数绑定变量的“开即用”支持,以及在映程序中传输对象填充逻辑分离?/p> Spring JDBC的优点应该促使您向这一框架UL现有的代码。希望本文在q一斚w能有所帮助。它会帮助您获得一些重构工具和知识。例如,如果您没有采用P2I Extract InterfaceQ那么可以用重构,从现有的DAO实现cd建接口。除此之外,查看本文的参考资料可以得到更多指对{?/p>
关键? spring
ApplicationContext是Spring的核心,Context我们通常解释Z下文环境Q我想用“容器”来表述它更Ҏ理解一些,ApplicationContext则是“应用的容器”了:PQSpring把Bean攑֜q个容器中,在需要的时候,用getBeanҎ取出Q虽然我没有看过q一部分的源代码Q但我想它应该是一个类似Map的结构?
在Web应用中,我们会用到WebApplicationContextQWebApplicationContextl承自ApplicationContextQ先让我们看看在Web应用中,怎么初始化WebApplicationContextQ在web.xml中定?
以ContextLoaderListenerZQ我们可以看?
ContextLoader是一个工LQ用来初始化WebApplicationContextQ其主要Ҏ是initWebApplicationContextQ我们l追tinitWebApplicationContextq个ҎQ具体代码我不脓出,大家可以看Spring中的源码Q,我们发现Q原来ContextLoader是把WebApplicationContextQXmlWebApplicationContext是默认实现类Q放在了ServletContext中,ServletContext也是一个“容器”,也是一个类似Map的结构,而WebApplicationContext在ServletContext中的KEY是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTEQ我们如果要使用WebApplicationContext则需要从ServletContext取出QSpring提供了一个WebApplicationContextUtilsc,可以方便的取出WebApplicationContextQ只要把ServletContext传入可以了?
]]>
1、如何学习SpringQ?br />你可以通过下列途径学习springQ?br />(1) spring下蝲包中doc目录下的MVC-step-by-step和sample目录下的例子都是比较好的spring开发的例子?br />
(2) AppFuse集成了目前最行的几个开源轻量框架或者工具Ant,XDoclet,Spring,Hibernate(iBATIS),JUnit,Cactus,StrutsTestCase,Canoo's WebTest,Struts Menu,Display Tag Library,OSCache,JSTL,Struts ?br />你可以通过AppFuse源代码来学习spring?br />AppFuse|站Qhttp://raibledesigns.com/wiki/Wiki.jsp?page=AppFuse
(3)Spring 开发指?夏昕)Qhttp://www.xiaxin.net/Spring_Dev_Guide.rarQ?br />一本spring的入门书c?里面介绍了反转控制和依赖注射的概念,以及spring的bean理Qspring的MVCQspring和hibernteQiBatis的结合?br />
(4) spring学习的中文论?br />SpringFramework中文论坛(http://spring.jactiongroup.net)
Java视线论坛(http://forum.javaeye.com)的spring栏目
2、利用Spring框架~程Qconsole打印出log4j:WARN Please initialize the log4j system properlyQ?br />说明你的log4j.properties没有配置。请把log4j.properties攑ֈ工程的classpath中,eclipse的classpath为bin目录Q由于编译后src目录下的文g会拷贝到bin目录下,所以你可以把log4j.properties攑ֈsrc目录下?br />q里l出一个log4j.properties的例子:
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
3、出?java.lang.NoClassDefFoundError?
一般情况下是由于你没有把必要的jar包放到lib中?br />
比如你要采用spring和hibernateQ带事务支持的话Q,你除了spring.jar外还需要hibernat.jar、aopalliance.jar、cglig.jar、jakarta-commons下的几个jar包?br />
http://www.springframework.org/download.html下蝲spring开发包Q提供两Uzip?br />spring-framework-1.1.3-with-dependencies.zip和spring-framework-1.1.3.zipQ我你下载spring-framework-1.1.3-with-dependencies.zip。这个zip解压~后比后者多一个lib目录Q其中有hibernate、j2ee、dom4j、aopalliance、jakarta-commons{常用包?br />
4、java.io.FileNotFoundException: Could not open class path resource [....hbm.xml],提示找不到xml文gQ?br />原因一般有两个Q?br />(1)该xml文g没有在classpath中?br />(2)applicationContext-hibernate.xml中的xml名字没有带包名。比如:
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
<property name="mappingResources">
<list>
<value>User.hbm.xml</value> 错,改ؓQ?<value>com/yz/spring/domain/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect"> net.sf.hibernate.dialect.MySQLDialect </prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
5、org.springframework.beans.NotWritablePropertyException: Invalid property 'postDao' of bean classQ?br />出现异常的原因是在application-xxx.xml中property name的错误?br /><property name="...."> 中name的名字是与bean的setҎ相关的,而且要注意大写?br />比如
public class PostManageImpl extends BaseManage implements PostManage {
private PostDAO dao = null;
public void setPostDAO(PostDAO postDAO){
this.dao = postDAO;
}
}
那么xml的定义应该是Q?br /><bean id="postManage" parent="txProxyTemplate">
<property name="target">
<bean class="com.yz.spring.service.implement.PostManageImpl">
<property name="postDAO"><ref bean="postDAO"/></property> ?br /> <property name="dao"><ref bean="postDAO"/></property> ?br /> </bean>
</property>
</bean>
6、Spring中如何实C务管理?
首先Q如果用mysqlQ确定mysql为InnoDBcd?br /> 事务理的控制应该放到商业逻辑层。你可以写个处理商业逻辑的JavaBeanQ在该JavaBean中调用DAOQ然后把该Bean的方法纳入spring的事务管理?br />
比如Qxml文g定义如下Q?br /><bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="userManage" parent="txProxyTemplate">
<property name="target">
<bean class="com.yz.spring.service.implement.UserManageImpl">
<property name="userDAO"><ref bean="userDAO"/></property>
</bean>
</property>
</bean>
com.yz.spring.service.implement.UserManageImpl是我们的实现商业逻辑的JavaBean。我们通过parent元素</span>
7、如何管理Spring框架下更多的JavaBeanQ?br />
JavaBean多Qspring配置文gp大,q样不易l护。ؓ了配置清晰Q我们可以将JavaBean分类理Q放在不同的配置文g中?应用启动时将所有的xml同时加蝲?br />
比如Q?br />
DAO层的JavaBean攑ֈapplicationContext-hibernate.xml中,商业逻辑层的JavaBean攑ֈapplicationContext-service.xml中。然后启动类中调用以下代码蝲入所有的ApplicationContext?br />
String[] paths = {"com/yz/spring/dao/hibernate/applicationContext-hibernate.xml",
"com/yz/spring/service/applicationContext-service.xml"};
ctx = new ClassPathXmlApplicationContext(paths);
8、web应用中如何加载ApplicationContextQ?br />
可以通过定义web.xmlQ由web容器自动加蝲?br />
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?
9、在spring中如何配|的log4j?
在web.xml中加入以下代码即可?br />
Qcontext-paramQ?br />Qparam-nameQlog4jConfigLocationQ?param-nameQ?br />Qparam-valueQ?WEB-INF/log4j.propertiesQ?param-valueQ?br />Q?context-paramQ?
10、Spring框架入门的编E问题解决了Q我该如何更深地领会Spring框架呢?
q两本书你该ȝ看。这两本书是由Spring的作者Rod Johnson~写的?br />
Expert One on one J2EE Design and Development
Expert One on one J2EE Development Without EJB
你也该看看martinfowler的Inversion of Control Containers and the Dependency Injection pattern?br />
http://www.martinfowler.com/articles/injection.html
再好好研M下spring的文?br />
http://www.jactiongroup.net/reference/html/index.htmlQ中文版Q未全部译Q?
q有是多实践吧?br />
]]>
public interface IEmployeeDAO {
//SQL String that will be executed
public String FIND_BY_SAL_RNG = "SELECT EMP_NO, EMP_NAME, "
+ "SALARY FROM EMP WHERE SALARY >= ? AND SALARY <= ?";
//range. The input parameter is the immutable map object
//obtained from the HttpServletRequest. This is an early
//refactoring based on "Introduce Parameter Object"
}
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import com.bea.dev2dev.to.EmployeeTO;
{
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List empList = new ArrayList();
//Transfer Object for inter-tier data transfer
EmployeeTO tempEmpTO = null;
try{
//DBUtil - helper classes that retrieve connection from pool
conn = DBUtil.getConnection();
pstmt = conn.prepareStatement(FIND_BY_SAL_RNG);
pstmt.setDouble(1, Double.valueOf( (String)
salaryMap.get("MIN_SALARY") );
pstmt.setDouble(2, Double.valueOf( (String)
salaryMap.get("MIN_SALARY") );
rs = pstmt.executeQuery();
int tmpEmpNo = 0;
String tmpEmpName = "";
double tmpSalary = 0.0D;
while (rs.next()){
tmpEmpNo = rs.getInt("EMP_NO");
tmpEmpName = rs.getString("EMP_NAME");
tmpSalary = rs.getDouble("SALARY");
tempEmpTO = new EmployeeTO(tmpEmpNo,
tmpEmpName,
tmpSalary);
empList.add(tempEmpTO);
}//end while
}//end try
catch (SQLException sqle){
throw new DBException(sqle);
}//end catch
finally{
try{
if (rs != null){
rs.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
try{
if (pstmt != null){
pstmt.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
try{
if (conn != null){
conn.close();
}
}
catch (SQLException sqle){
throw new DBException(sqle);
}
}//end of finally block
return empList;
}//end method findBySalaryRange
}
上面的清单说明了DAOҎ的一些要点:
public class DAOFactory {
private static DAOFactory daoFac;
static{
daoFac = new DAOFactory();
}
private DAOFactory(){}
public DAOFactory getInstance(){
return daoFac;
}
public IEmployeeDAO getEmployeeDAO(){
return new EmployeeDAOImpl();
}
}
IEmployeeBusinessService {
.getEmployeeDAO();
List empList = empDAO.findBySalaryRange(salaryMap);
return empList;
}
}
问题
import org.springframework.jdbc.core.JdbcTemplate;
implements IEmployeeDAO{
salaryMap.get("MIN_SALARY"))
,Double.valueOf((String)
salaryMap.get("MAX_SALARY"))
};
//The getJdbcTemplate method of JdbcDaoSupport returns an
//instance of JdbcTemplate initialized with a datasource by the
//Spring Bean Factory
JdbcTemplate daoTmplt = this.getJdbcTemplate();
return daoTmplt.queryForList(FIND_BY_SAL_RNG,dblParams);
}
}
W二步:修改业务服务- 现在业务服务实现了一个新ҎsetDaoQ)QSpring容器使用该方法传递DAO实现cȝ引用。该q程UCؓ“设|方法注入(setter injectionQ”,通过W三步中的配|文件告知Spring容器该过E。注意,不再需要用DAOFactoryQ因为Spring BeanFactory提供了这功能: public class EmployeeBusinessServiceImpl
implements IEmployeeBusinessService {
IEmployeeDAO empDAO;
public List getEmployeesWithinSalaryRange(Map salaryMap){
List empList = empDAO.findBySalaryRange(salaryMap);
return empList;
}
public void setDao(IEmployeeDAO empDAO){
this.empDAO = empDAO;
}
}
"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- Configure Datasource -->
<bean id="FIREBIRD_DATASOURCE"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiEnvironment">
<props>
<prop key="java.naming.factory.initial">
weblogic.jndi.WLInitialContextFactory
</prop>
<prop key="java.naming.provider.url">
t3://localhost:7001
</prop>
</props>
</property>
<property name="jndiName">
<value>
jdbc/DBPool
</value>
</property>
</bean>
<bean id="EMP_DAO" class="com.bea.dev2dev.dao.EmployeeDAOImpl">
<property name="dataSource">
<ref bean="FIREBIRD_DATASOURCE"></ref>
</property>
</bean>
<bean id="EMP_BUSINESS"
class="com.bea.dev2dev.sampleapp.business.EmployeeBusinessServiceImpl">
<property name="dao">
<ref bean="EMP_DAO"></ref>
</property>
</bean>
</beans>
import junit.framework.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
private IEmployeeBusinessService empBusiness;
private Map salaryMap;
List expResult;
initSpringFramework();
initSalaryMap();
initExpectedResult();
}
private void initExpectedResult() {
expResult = new ArrayList();
Map tempMap = new HashMap();
tempMap.put("EMP_NO",new Integer(1));
tempMap.put("EMP_NAME","John");
tempMap.put("SALARY",new Double(46.11));
expResult.add(tempMap);
}
private void initSalaryMap() {
salaryMap = new HashMap();
salaryMap.put("MIN_SALARY","1");
salaryMap.put("MAX_SALARY","50");
}
private void initSpringFramework() {
ApplicationContext ac = new FileSystemXmlApplicationContext
("C:/SpringConfig/Spring-Config.xml");
empBusiness =
(IEmployeeBusinessService)ac.getBean("EMP_BUSINESS");
}
protected void tearDown() throws Exception {
}
* Test of getEmployeesWithinSalaryRange method,
* of class
* com.bea.dev2dev.business.EmployeeBusinessServiceImpl.
*/
public void testGetEmployeesWithinSalaryRange() {
List result = empBusiness.getEmployeesWithinSalaryRange
(salaryMap);
assertEquals(expResult, result);
}
}
public interface IEmployeeDAO {
public String FIND_BY_SAL_RNG = "SELECT EMP_NO, EMP_NAME, "
+ "SALARY FROM EMP WHERE SALARY >= :max AND SALARY <= :min";
//The input parameter is the immutable map object obtained from
//the HttpServletRequest. This is an early refactoring based on
//- "Introduce Parameter Object"
}
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
implements IEmployeeDAO{
this.getNamedParameterJdbcTemplate();
return tmplt.queryForList(IEmployeeDAO.FIND_BY_SAL_RNG
,salaryMap);
}
} private void initSalaryMap() {
salaryMap = new HashMap();
salaryMap.put("MIN_SALARY",new Double(1));
salaryMap.put("MAX_SALARY",new Double(50));
}
Spring DAO回调函数
private String empName;
private double salary;
public EmployeeTO(int empNo,String empName,double salary) {
this.empNo = empNo;
this.empName = empName;
this.salary = salary;
}
public String getEmpName() {
return this.empName;
}
public int getEmpNo() {
return this.empNo;
}
public double getSalary() {
return this.salary;
}
public boolean equals(EmployeeTO empTO){
return empTO.empNo == this.empNo;
}
}
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
throws SQLException{
int empNo = rs.getInt(1);
String empName = rs.getString(2);
double salary = rs.getDouble(3);
EmployeeTO empTo = new EmployeeTO(empNo,empName,salary);
return empTo;
}
}
implements IEmployeeDAO{
getNamedParameterJdbcTemplate();
return daoTmplt.query(IEmployeeDAO.FIND_BY_SAL_RNG, salaryMap,
new EmployeeTOMapper());
}
}
private Map salaryMap;
List expResult;
// same as in the previous example
private void initExpectedResult() {
expResult = new ArrayList();
EmployeeTO to = new EmployeeTO(2,"John",46.11);
expResult.add(to);
}
* Test of getEmployeesWithinSalaryRange method, of
* class com.bea.dev2dev.business.
* EmployeeBusinessServiceImpl
*/
public void testGetEmployeesWithinSalaryRange() {
List result = empBusiness.
getEmployeesWithinSalaryRange(salaryMap);
assertEquals(expResult, result);
}
EmployeeTO expTO = (EmployeeTO) expResult.get(0);
EmployeeTO actualTO = (EmployeeTO) result.get(0);
if(!expTO.equals(actualTO)){
throw new RuntimeException("** Test Failed **");
}
}
}
]]>
q是在网上发现的一关于Spring AOP~程的教E,dq篇文章后,Spring AOP不再难以理解Q推荐给Spring AOP的初学者。这是译文的链接?
AOP正在成ؓ软g开发的下一个圣杯。用AOPQ你可以处理aspect的代码注入主E序Q通常ȝ序的主要目的q不在于处理q些aspect。AOP可以防止代码混ؕ?
Z理解AOP如何做到q点Q考虑一下记日志的工作。日志本w不太可能是你开发的ȝ序的主要d。如果能“不可见的”、通用的日志代码注入主E序中,那该多好啊。AOP可以帮助你做到?
Spring framework是很有前途的AOP技术。作ZU非늕性的Q轻型的AOP frameworkQ你无需使用预编译器或其他的元标{,便可以在JavaE序中用它。这意味着开发团队里只需一对付AOP frameworkQ其他hq是象往怸LE?
AOP是很多直觉难以理解的术语的根源。幸q的是,你只要理解三个概念,可以编写AOP模块。这三个概念是:adviceQpointcut和advisor。advice是你惛_别的E序内部不同的地Ҏ入的代码。pointcut定义了需要注入advice的位|,通常是某个特定的cȝ一个publicҎ。advisor是pointcut和advice的装配器Q是advice注入ȝ序中预定义位|的代码?
既然我们知道了需要用advisor向主要代码中注入“不可见的”adviceQ让我们实现一个Spring AOP的例子。在q个例子中,我们实C个before adviceQ这意味着advice的代码在被调用的publicҎ开始前被执行。以下是q个before advice的实C码:
代码:
package com.company.springaop.test;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class TestBeforeAdvice implements MethodBeforeAdvice {
public void before(Method m, Object[] args, Object target)
throws Throwable {
System.out.println("Hello world! (by "
+ this.getClass().getName()
+ ")");
}
}
接口MethodBeforeAdvice只有一个方法before需要实玎ͼ它定义了advice的实现。beforeҎq三个参数Q它们提供了相当丰富的信息。参数Method m是advice开始后执行的方法。方法名U可以用作判断是否执行代码的条g。Object[] args是传l被调用的publicҎ的参数数l。当需要记日志Ӟ参数args和被执行Ҏ的名Uͼ都是非常有用的信息。你也可以改变传lm的参敎ͼ但要心使用q个功能Q编写最初主E序的程序员q不知道ȝ序可能会和传入参数的发生冲突。Object target是执行方法m对象的引用?
在下面的BeanImplcMQ每个publicҎ调用前,都会执行adviceQ?
代码:
package com.company.springaop.test;
public class BeanImpl implements Bean {
public void theMethod() {
System.out.println(this.getClass().getName()
+ "." + new Exception().getStackTrace()[0].getMethodName()
+ "()"
+ " says HELLO!");
}
}
cBeanImpl实现了下面的接口BeanQ?
代码:
package com.company.springaop.test;
public interface Bean {
public void theMethod();
}
虽然不是必须使用接口Q但面向接口而不是面向实现编E是良好的编E实践,Spring也鼓p样做?
pointcut和advice通过配置文g来实玎ͼ因此Q接下来你只需~写L法的Java代码Q?
代码:
package com.company.springaop.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//Read the configuration file
ApplicationContext ctx
= new FileSystemXmlApplicationContext("springconfig.xml");
//Instantiate an object
Bean x = (Bean) ctx.getBean("bean");
//Execute the public method of the bean (the test)
x.theMethod();
}
}
我们从读入和处理配置文g开始,接下来马上要创徏它。这个配|文件将作ؓ_合E序不同部分的“胶水”。读入和处理配置文g后,我们会得C个创建工厂ctx。Q何一个Spring理的对象都必须通过q个工厂来创建。对象通过工厂创徏后便可正怋用?
仅仅用配|文件便可把E序的每一部分l装h?
代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--CONFIG-->
<bean id="bean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.company.springaop.test.Bean</value>
</property>
<property name="target">
<ref local="beanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>theAdvisor</value>
</list>
</property>
</bean>
<!--CLASS-->
<bean id="beanTarget" class="com.company.springaop.test.BeanImpl"/>
<!--ADVISOR-->
<!--Note: An advisor assembles pointcut and advice-->
<bean id="theAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="theBeforeAdvice"/>
</property>
<property name="pattern">
<value>com\.company\.springaop\.test\.Bean\.theMethod</value>
</property>
</bean>
<!--ADVICE-->
<bean id="theBeforeAdvice" class="com.company.springaop.test.TestBeforeAdvice"/>
</beans>
四个bean定义的次序ƈ不重要。我们现在有了一个adviceQ一个包含了正则表达式pointcut的advisorQ一个主E序cd一个配|好的接口,通过工厂ctxQ这个接口返回自己本w实现的一个引用?
BeanImpl和TestBeforeAdvice都是直接配置。我们用一个唯一的ID创徏一个bean元素Qƈ指定了一个实现类。这是全部的工作?
advisor通过Spring framework提供的一个RegexMethodPointcutAdvisorcL实现。我们用advisor的一个属性来指定它所需的advice-bean。第二个属性则用正则表辑ּ定义了pointcutQ确保良好的性能和易L?
最后配|的是beanQ它可以通过一个工厂来创徏。bean的定义看h比实际上要复杂。bean是ProxyFactoryBean的一个实玎ͼ它是Spring framework的一部分。这个bean的行为通过一下的三个属性来定义Q?
Spring工具
虽然你可以手工修改Ant构徏脚本Q但使用SpringUIQ译注:SpringUI现在是Spring framework的一部分Qƈ改名为spring-ideQ,使用Spring AOP变得很简单,只要点点鼠标卛_。你可以把SpringUI安装成Eclipse的一个plug-in。然后,你只需在你的project上右击鼠标,q择“add Spring Project Nature”。在project属性中Q你可以在“Spring Project”下dSpring配置文g。在~译前把下面的类库加入projectQaopalliance.jarQcommons-logging.jarQjakarta-oro-2.0.7.jar和spring.jar。运行程序时你会看到下面的信息:
... (logging information)
Hello world! (by com.company.springaop.test.TestBeforeAdvice)
com.company.springaop.test.BeanImpl.theMethod() says HELLO!
优点和缺?/span>
Spring比v其他的framework更有优势Q因为除了AOP以外Q它提供了更多别的功能。作Z个轻型frameworkQ它在J2EE不同的部分都可以发挥作用。因此,即不想使用Spring AOPQ你可能q是想用Spring。另一个优ҎQSpringq不要求开发团队所有的人员都会用它。学习Spring应该从Spring reference的第一开始。读了本文后Q你应该可以更好地理解Spring reference了。Spring唯一的缺Ҏ~Z更多的文,但它的mailing list是个很好的补充,而且会不断地出现更多的文?/span>
]]>
?/SPAN>J2EE应用开发中Q经帔R到的问题是Q如何将不同的组件组装成Z个内聚的应用E序Q?/SPAN>IoC模式可以解决q个问题Q其目标是将lg的配|与使用分离开?/SPAN>
IoCQ?/SPAN>Inversion of ControlQ控制反?/SPAN>[1]Q其原理是基?/SPAN>OO设计原则?/SPAN>The Hollywood PrincipleQ?/SPAN>Don't call us, we'll call you。也是_所有的lg[2]都是被动的(PassiveQ,所有的lg初始化和调用都由容器负责。组件处在一个容器当中,由容器负责管理?/SPAN>
要说?/SPAN>IoC模式最好的Ҏ是用代码。下Ҏ一D|常的代码?/SPAN>
class ClassA...
public String aMethod(String arg){
String result = instanceOfClassB.bMethod();
do something;
return result;
}
在上边的代码里,我们要解决的问题是:ClassA如何获得ClassB的实例?一个最直接的方法是?/SPAN>aMethod里声明:
IClassB instanceOfClassB = new ClassB();
q里使用了一个接?/SPAN>IClassB?/SPAN>
问题是,如果出现q样的情况:l箋使用ClassAQ但要求?/SPAN>IClassB的另一个实?/SPAN>ClassB2代替ClassB呢?更概括一点说Q?/SPAN>ClassA怎样才能扑ֈIClassB的具体实玎ͼ很明显,上述代码增加ClassA?/SPAN>ClassB的耦合度,以致于无法在不修?/SPAN>ClassA的情况下变更IClassB的具体实现?/SPAN>
IoC模式是用于解决q样的问题。当Ӟq有其他的方法,比如Service Locator模式Q但现在我们只关?/SPAN>IoC。如前所qͼIoC容器负责初始化组Ӟ?/SPAN>IClassBQ,q将实例交给使用者。用代码或配置文g以声明的方式接口与实例兌hQ?/SPAN>IoC容器负责q行实际的调用处理。对于调用者,只需要关注接口就行了?/SPAN>
Ҏ实例传入方式的不同,IoC分ؓtype 1 IoCQ接口注?/SPAN>[3]Q?/SPAN>type 2 IoCQ设值方法注入)?/SPAN>type 3 IoCQ构造子注入Q。分别用代码说明如下Q?/SPAN>
type 1 IoCQ接口注入)
public interface GetClassB {
void getClassB(IClassB instanceOfClassB);
}
class ClassA implements GetClassB?/FONT>
IClassB instanceOfClassB;
void getClassB(IClassB instanceOfClassB) {
this.instanceOfClassB = instanceOfClassB;
}
type 2 IoCQ设值方法注入)
class ClassA...
IClassB instanceOfClassB;
public void setFinder(IClassB instanceOfClassB) {
this.instanceOfClassB = instanceOfClassB;
}
type 3 IoCQ构造子注入Q?/SPAN>
class ClassA?/FONT>
ClassB instanceOfClassB;
public classA(IClassB instanceOfClassB) {
this. instanceOfClassB = instanceOfClassB;
}
Spring使用的是type 2 IoC?/SPAN>[1] ?/SPAN>Martin Fowler?/SPAN>Inversion of Control Containers and the Dependency Injection pattern一文中Q作者提出本模式更准的名称应该?/SPAN>Dependency Injection。考虑C用上的习惯,在本文中我们将l箋使用IoC的概c?/SPAN>
[2] 同一文章中Q?/SPAN>Martin Fowlerq提ZComponetQ组Ӟ?/SPAN>ServiceQ服务)的区别问题。我个h认ؓ“组件”更有助于帮助我们理?/SPAN>IoC的概念,所以在本文档中使用“组件”来代表“组件或服务?/SPAN>
[3] 如上所qͼ注入是另一U说法,此处只用于辅助说明?/SPAN>
资源链接?/SPAN>
Spring Framework Documentaion
http://www.springframework.org/documentation.html
Spring中文论坛图书?/SPAN>
http://xglw.51.net/5team/springframework/viewtopic.php?t=31
Spring Reference Documentation
http://www.springframework.org/docs/reference/index.html
or http://www.springframework.org/docs/spring-reference.pdf
作者:Spring 开发组
中文?/SPAN>http://xglw.51.net/5team/springframework/book/spring_referece_inchinese_m2.pdfQ?/SPAN>for M2Q?/SPAN>
译Q?/SPAN>Spring中文论坛译l?/SPAN>
Spring?/SPAN>Document中还提供了:
The Spring Framework - A Lightweight Container by Rod Johnson, Juergen Hoeller
Container Resources vs Local Resources(for transactional data access with the Spring Framework) by Juergen Hoeller
Data Access and Transaction Abstraction with the Spring Framework(featuring Hibernate examples) by Juergen Hoeller
Web MVC with the Spring Framework by Juergen Hoeller
中文版:http://xglw.51.net/5team/springframework/book/webMvcSpring.pdf
译Q?/SPAN>yanger
Q?/SPAN>more?/FONT>Q?/SPAN>
Introducing the Spring Framework
http://www.theserverside.com/articles/article.tss?l=SpringFramework
作者:Rod Johnson
中文?/SPAN> http://xglw.51.net/5team/springframework/viewtopic.php?t=18
译Q?/SPAN>yanger
Developing a Spring Framework MVC application step-by-step
http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step.html
作者:Thomas Risberg
Spring Framework Tutorial
http://www.springframework.org/downloads/tutorial.pdf
作者:Isabelle Muszynski
?/SPAN>Spring In Chinese Group?/SPAN>
Q?/SPAN>PPTQ?/SPAN> On Spring by gigix
Q?/SPAN>PPTQ?/SPAN> Spring Introduction for 3.12M2 by yanger
Framework Design
http://qca.cn/frameworkdesign/content.htm
作者:林星
书籍购买链接
Expert One-on-One J2EE Design and Development
Q略Q?/SPAN>
作者:Rod Johnson
中文版:http://www.china-pub.com/computers/common/info.asp?id=14126
Q说明:英文版本w很明,译则评L差)
我眼中的Spring
http://www.blogbus.com/blogbus/blog/diary.php?diaryid=125334
作者:dreamhead
Spring is coming
http://www.blogbus.com/blogbus/blog/diary.php?diaryid=126737
作者:founder_chen
分析ApplicationContext
Spring的bean包支持通过~码方式理和操作bean的基本功能,ApplicationContext
则以Framework的方式提供BeanFactory的所有功能。用ApplicationContextQ你可以?/FONT>
pȝ加蝲你的beanQ例如,在Servlet容器初始化ContextLoaderServletӞ通过
ContextLoadercd载Spring FrameworkQ,而不是用编码方式来加蝲?/FONT>
ApplicationContext接口是context包的基础Q位?/FONT>
org.springframework.context包里Q提供了BeanFactory的所有功能。除此之外,
ApplicationContextZ支持Framework的工作方式,提供了以下的功能Q?/FONT>
l.MessageSourceQ提供了语言信息的国际化支持 2.提供资源Q如URL和文件系l)的访问支? 3.为实CApplicationListener接口的bean提供了事件传播支? 4.Z同的应用环境提供不同的contextQ例如支持web应用的XmlWebApplicationContextc? 下面的源代码分析主要集中在ApplicationContext接口Ҏ的功能上Q如国际化支持,
资源讉K和bean的事件传播? 我的问题
现在我的问题是,ApplicationContext是如何实C面提到的功能的?下面的分?/FONT>
作出回{? 准备试用例
1. 首先在类路径根目录编写测试国际化支持的testmsg.xmlQƈ它加入Spring IDE
的管理范_
<beans> <bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>message</value> </list> </property> </bean> </beans> 2. ~写试用例Q测试国际化支持和资源访问的功能? public class MsgTest extends TestCase { ApplicationContext ctx = null; public MsgTest(String arg0) { super(arg0); } protected void setUp() throws Exception { super.setUp(); ctx = new FileSystemXmlApplicationContext("testmsg.xml"); } public void testMessageResource() { Object[] args = {"?, "?}; String msg = ctx.getMessage("hello", args, Locale.CHINA); //System.out.println("msg=" + msg); assertEquals("我和?, msg); Resource rs = ctx.getResource("classpath:log4j.properties"); assertTrue(rs.exists()); } } 3. 在类路径根目录创建属性文件message.propertiesQ内容ؓhello={0}和{1}? 此时q行TestCaseQ果然不出所料,Junit视图的测试状态是U条。将打印msg变量?/FONT>
语句打开Q重新测试,发现"?字是q?/FONT>
在message.properties文g中将"?字改为ASCII码\u548cQ重新运行TestCaseQ?/FONT>
l条Q测试通过?/FONT>
testmsg.xml中bean的id改ؓmessageSource1Q重新运行测试,出现U条Q?/FONT>
试p|Q说明bean的名U必LmessageSourceQ这点值得注意。至于其中的原因E后
说明? ApplicationContext接口通过l承BeanFactoryQMessageSource和ResourceLoader
三个接口Q分别支持管理和操作bean的功能,语言信息的国际化支持以及对资源访问的支持?/FONT>
AbstractApplicationContect是ApplicationContext的抽象实现类Q它的承层ơ较
为紊乱,我觉得这里应该进行代码重构。AbstractXmlApplicationContext?/FONT>
AbstractApplicationContext的子c,提供了对XML配置文g的支持,它有三个子类Q分?/FONT>
用于不同的应用环境?/FONT>
对于MessageSourceQSpring提供了两个bean实现QResourceBundleMessageSource?/FONT>
ReloadableResourceBundleMessageSource。前者提供了讉KProperties文g的支持,后?/FONT>
d了无需重启JVMQ重新加载Properties文g的支持?/FONT>
ApplicationContext的国际化和资源访问支?/FONT>
1. 如类层次图所C,在我们的例子中,FileSystemXmlApplicationContext使用
DefaultListableBeanFactory装蝲和解释testmsg.xml配置文gQ参见代码分析的
BeanFactory部分Q? 2. FileSystemXmlApplicationContextҎ配置文g的BeanDefinition创徏
ResourceBundleMessageSourceQ加?lt;list>元素定义的Properties文gQƈ保存?/FONT>
AbstractApplicationContext的属性中。当客户E序调用getMessageҎӞ
AbstractApplicationContext调用ResourceBundleMessageSource的getMessageҎq回
Message信息?
3. 至于上节提到的MessageSource的id只能是messageSourceQ是因ؓ
AbstractApplicationContext的initMessageSource()Ҏ中,有这样一D代码:
this.messageSource = (MessageSource) getBean(MESSAGE_SOURCE_BEAN_NAME);
其中MESSAGE_SOURCE_BEAN_NAME的定义ؓQ?/FONT>
static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
原因扑ֈ了,其实只要E做代码重构Q即可消除这个缺陗?/FONT>
4. 如类层次图所C,AbstractApplicationContextl承了DefaultResourceLoaderQ?/FONT>
当客L序调用getResourceҎӞ使用父类中实现的Ҏ来处理?/FONT>
ApplicationContext的事件传?/FONT>
准备试用例
1. 首先~写试用例? public class SenderBeanTest extends TestCase { ApplicationContext ctx = null; protected void setUp() throws Exception { super.setUp(); ctx = new FileSystemXmlApplicationContext("testlistener.xml"); } public void testSendEmail() { SenderBean sender = (SenderBean)ctx.getBean("sender"); String msg = sender.sendMessage("test message"); assertEquals("test message", msg); } }
2. 接着~写testlistener.xml配置文g? <beans> <bean id="sender" class="unittest.SenderBean"/> <bean id="listener" class="unittest.MessageListener"/> </beans>
3. 最后编写SenderBeanQMessageListener和MessageEventcR? public class SenderBean implements ApplicationContextAware { private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public String sendMessage(String msg) { MessageEvent event = new MessageEvent(msg); this.applicationContext.publishEvent(event); return msg; } } public class MessageListener implements ApplicationListener { public void onApplicationEvent(ApplicationEvent event) { if (event instanceof MessageEvent) { System.out.println("I got the message:" + event.getSource()); } } } public class MessageEvent extends ApplicationEvent { public MessageEvent(Object source) { super(source); System.out.println(this.getTimestamp() + ":" + source); } }
q行试案例SenderBeanTestQ绿条,试通过。ConsoleH口出现以下DEBUG
信息Q?/FONT>
…? 796 DEBUG support.DefaultListableBeanFactory - Returning
cached instance of singleton bean 'sender' 1085553911796:test message 796 DEBUG support.FileSystemXmlApplicationContext - Publishing event
in context [org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=13549765]:
unittest.MessageEvent[source=test message] I got the message:test message 事g传播的实? 1. FileSystemXmlApplicationContext的构造器调用AbstractApplicationContext?/FONT>
refreshҎ。如图所C,refreshҎ调用refreshListenersҎ?/FONT>
2. AbstractApplicationContext的refreshListenersҎ使用BeanFactory?/FONT>
getBeanOfTypeҎ得到所有ApplicationListenerc(本例中是MessageListenerQ,q?/FONT>
addListenerҎ把它们都攑օApplicationEventMulticasterImpl的Set容器?/FONT>
QeventListeners变量Q?/FONT>
3. 如图所C,SenderBean实现ApplicationContextAware接口Qƈ通过
setApplicationContextҎ注入ApplicationContext对象实例?/FONT>
4. 当调用SenderBeancsendMessageҎӞAbstractApplicationContext调用
publishEventҎ?/FONT>
5. AbstractApplicationContextcȝpublishEventҎ调用
ApplicationEventMulticasterImplcȝonApplicationEventҎ?/FONT>
6. ApplicationEventMulticasterImpl通知Set容器中所有的ApplicationListener
对象Q调用它们的onApplicationEventҎ?/FONT>
从以上的q程可以看出QApplicationContext事仉知所有的
ApplicationListener。如果ApplicationListener的子c(如MessageListenerQ只x?/FONT>
指定的事件类型,需要自q写过滤代码,如例子中的if (event instanceof MessageEvent)?/FONT>
/**
作者:Willpower
来源QRifoo TechnologyQ?A >http://www.rifoo.comQ?BR>旉Q?005-12-25
备注Q{载请保留以上声明
**/
q本书是一个以代码和实战ؓȝ书,全书在构Z个过pR订购pȝQ体育商店可以用来对它们的过pRq行理和订购。第一节作者先编码了两个有依赖关pȝcCommandLineView.java和RentABike.java。我们先看看源代码:
Example 1-1. Bike.java
public class Bike {
private String manufacturer;
private String model;
private int frame;
private String serialNo;
private double weight;
private String status;
public Bike(String manufacturer, String model, int frame,
String serialNo, double weight, String status) {
this.manufacturer = manufacturer;
this.model = model;
this.frame = frame;
this.serialNo = serialNo;
this.weight = weight;
this.status = status;
}
public String toString( ) {
return "Bike : " +
"manufacturer -- " + manufacturer +
"\n: model -- " + model +
"\n: frame -- " + frame +
"\n: serialNo -- " + serialNo +
"\n: weight -- " + weight +
"\n: status -- " + status +
".\n";
}
public String getManufacturer( ) { return manufacturer; }
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getModel( ) { return model; }
public void setModel(String model) { this.model = model; }
public int getFrame( ) { return frame; }
public void setFrame(int frame) { this.frame = frame; }
public String getSerialNo( ) { return serialNo; }
public void setSerialNo(String serialNo) { this.serialNo = serialNo; }
public double getWeight( ) { return weight; }
public void setWeight(double weight) { this.weight = weight; }
public String getStatus( ) { return status; }
public void setStatus(String status) { this.status = status; }
}
可以看出QBike.java是一个普通的JAVABEANQ用来表qC个过pR实体Q包括制造商Q型P规格Q序列号Q重量和状态等树型?BR>
Example 1-2. RentABike.java
import java.util.*;
public class RentABike {
private String storeName;
final List bikes = new ArrayList( );
public RentABike(String storeName) {
this.storeName = storeName;//商店?BR> //dq山车到数组
bikes.add(new Bike("Shimano", "Roadmaster", 20, "11111", 15,
"Fair"));
bikes.add(new Bike("Cannondale", "F2000 XTR", 18, "22222",12,
"Excellent"));
bikes.add(new Bike("Trek","6000", 19, "33333", 12.4,
"Fair"));
}
public String toString( ) { return "RentABike: " + storeName; }
//得到全部q山?BR> public List getBikes( ) { return bikes; }
//得到某一个序列号的过pR
public Bike getBike(String serialNo) {
Iterator iter = bikes.iterator( );
while(iter.hasNext( )) {
Bike bike = (Bike)iter.next( );
if(serialNo.equals(bike.getSerialNo( ))) return bike;
}
return null;
}
}
q个cL叙了U用一台过pR的操作,storeName是传入的商店名称。它对客L来说是一个门面,把所U用的过pR都放在一个List数组中存放v来,然后对外提供getBikes和getBike两个ҎQ可以让客户端知道目前所U用的所有过pR和某一个序列号的过pR是什么?BR>
我们再看看用h口是如何调用的:
Example 1-3. CommandLineView.java
import java.util.*;
public class CommandLineView {
private RentABike rentaBike;
public CommandLineView( ) {rentaBike = new RentABike("Bruce's Bikes"); }
public void printAllBikes( ) {
System.out.println(rentaBike.toString( ));
//调用门面的方?BR> Iterator iter = rentaBike.getBikes( ).iterator( );
while(iter.hasNext( )) {
Bike bike = (Bike)iter.next( );
System.out.println(bike.toString( ));
}
}
public static final void main(String[] args) {
CommandLineView clv = new CommandLineView( );
clv.printAllBikes( );
}
}
q行l果Q?BR>
C:\RentABikeApp\out> java CommandLineView
RentABike: Bruce's Bikes
Bike : manufacturer -- Shimano
: model -- Roadmaster
: frame -- 20
: serialNo -- 11111
: weight -- 15.0
: status -- Fair.
Bike : manufacturer -- Cannondale
: model -- F2000 XTR
: frame -- 18
: serialNo -- 22222
: weight -- 12.0
: status -- Excellent.
Bike : manufacturer -- Trek
: model -- 6000
: frame -- 19
: serialNo -- 33333
: weight -- 12.4
: status -- Fair.
大家看出问题了吗Q?BR>1 门面RentABike静态的创徏了一个商店里的租用的q山车,所以到时候如果一个新的过pR被引入的话,那么需要手工修改RentABike的代码,比如我们再加一个Bike2.javaQ属性和Bike.java不一栗我们还需要在RentABike的类里实例化它才行,q样造成了硬~码
2 q个模型Bike.java是很难测试的Q因为Bikes数组是固定的
3 q用h口和门面之间是强耦合的,它们存在一个硬~码的依赖,大家注意CommandLineView.java中的q两行代码:
private RentABike rentaBike;
public CommandLineView( ) {rentaBike = new RentABike("Bruce's Bikes"); }
所以,q一D늨序虽然能够完成一个简单的U用q山车的操作Q但是却不是一个易l护和扩展的?/FONT>
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=567619
Web层的Tapestry负责数据输入输出, 响应用户事gQ及输入校验的工? 通过讉K预先加蝲的WebApplicationContext(由Spring提供, 包含着所有Service bean)获得Service层的Service Bean, 把业务操作都委托l它?
Service层的bean则负责use case逻辑, domain相关的逻辑委托ldomain model中的beand? Service通过DAO完成对domain model的持久化工作. Service负责数据库事务和Hibernate Session的管?通过Spring的声明式事务理和与之集成的Hibernate Session理). Service层的另一w要工作是权限和访问控制?
Domain model负责表示问题域的数据和domain logic. DAO使用Hibernate持久化数据以及查? 在实现DAO? 我们使用了Spring的Hibernate DAO Support,极大地简化了代码, 很多Ҏ都只用简单的一行完? 有意思的? 最后完成的HibernateDAO的代码量居然比我写的MockDAO的代码少了一半还?
q样的架构优点很明显, 层次清晰, 各层的职责也明确, 便于分层设计与开? l合mock和spring的IOC, unit test也是非常Ҏ? 而且后台(Service, domain model and DAO)的代码不依赖于Web容器或是EJB容器的API, UL性非常好, 同样的代码可以在Web app中用也可在普通的Java app中? 只需更换UI?
按照q个整合的构Ӟ我们实现一个简单的实例Q实C列表分页查询和显C,数据增删改,ZHibernate Criteria Query提供了一个比较通用的查询机制。利用Middlegen和Velocity我们可以从已l徏好的数据库表l构自动生成Hibernate映射文gQ实体类和DAOQ极大地减少了工作量。我们还对这个小例子q行了压力测试(试时的数据量ؓ10万条记录Q,定q_不存在性能问题?
通过q个实例我们把整个架构基本走通一遍,qȝ了用这套架构开发时适用的开发流E和需要做的工作?
困扰的问?
问题1Q要不要使用DTOQ?
在上面的架构中我们ƈ没有明确Service和Web层间的数据传输是如何q行的。我们讨论好久要不要使用DTOQ最后的l论是不用?
使用DTO有两个主要的理由Q?BR>1、减Web层和Service层间的方法调用,通过一个方法调用就Web需要的数据都传lWeb?BR>2、隔domain model和Web层?
W一个理由在当前架构下是不成立的。因为我们的架构是集中式的,Web和Service是在同一个JVM中,它们之间的方法调用是没有EJBq程讉K的巨大消耗的?
W二个理p是需要考虑的。如果允许把domain model中的对象传给Web层,那么修改domain modelQ就会媄响到Web层?BR>如果使用DTOQ那么domain model实现上的变化׃会媄响到Web。但是大量的变化不是domain model实现上的变化Q而是domain model接口的变化,比如一个domain model的对象上d了一个属性,而这个属性需要用户修改,那么q时候必M改Web层,不管是不是用了DTO?BR>而且使用DTOQ就需要维护着一大堆对象Q或是它们的生成器,q是非常无聊、且Ҏ出错的工作?
Zq些考虑Q没有必要用DTO, 直接把domain object传给Tapestry的web层,利用Tapestry提供强大的数据绑定和lg功能很方?/P>
问题2QEntity like domain model or rich domain model?
我们使用Middlegen自动生成Hibernate映射文gQEntitycdDAOc? 但是生成的Entity只含有简单的属性和getter, setterҎ?BR>因此我们遇到了一个问题:我们的domain modelq要不要包含domain logicQ如果包含,那么和自动生成工具如何结合?
虽然一个rich domain model可以减少Service中的重复代码Q提高复用性?但是如何同自动生成结合?
或许可以使用<meta>标签Q生成抽象基c,我们l承q些自动生成的基c,d业务Ҏ?BR>但是单纯从结构的分离角度来考虑, domain model不应该包括复杂的domain logic, 只是作ؓ一个data model bean, 再加上一些简单的logic, 比如addChild的同Ӟ讄child的parent此类单logic
而且在POJO里面塞太多业务逻辑会导致Hibernate产生你预想不到的后果?
问题3QModel driven or Data driven?
q其实是一个很无聊的问? 采用Model drivenq是Data driven的方式一直都处于热烈的讨Z。我们主要是受到Rod Johnson(Expert one-on-one J2EE Design and Development 一书的作? 的媄响,采用了Data driven的方式。先作数据库设计Q生成库表,然后用Middlegen反向生成Hibernate映射文g和Entity及DAO?BR>但是在进入项目应用之后发现这U方法有可能会出C个问题:
aQ?数据库设计仅说明了系l要理的静态数据,我们q是得作面向对象分析Q以反映pȝ的动态行为。特别是当系l的业务不仅仅是单的CRUD操作Ӟq个问题更严重?
bQ?数据库设计ؓ了优化性能Q可能会把好几个应该是单独实体的数据攑օ一个实体中。这样如果直接把q种极粗_度实体映射成Entityc,那简直是不可接受的?BR>面向对象的分析设计模型得到的c都是相当细_度的。这U情况还得作面向对象的分析,明确到底q个_粒度的大表应该映射成那几个l粒度的对象?
如果采用Model drivenQ则可以用AndroMDA生成domain modelQHibernate DAOQHibernate mappingQ数据库表,单的Service和前台的Tapestry面?
不过换个角度分析, 采用哪种设计模式只是个h习惯而已Q无论是Model driven或者Data drivenQ对于相同的需求,2者最l的设计应该都是很类似的?BR>更周全的做法大概可以?边同时考虑的,一边做ModelQ一边还要考虑数据库的l构Q?再进行相应的调整Q用下来也没有什么问题。没有用代码生成Q?
只是用Hibernate的Eclipse plugin (Hibernate Synchronizer)来帮我做一些code assist. 也可以用xDoclet来生成mapping文g和DDL.
问题4QHibernate Session生命周期如何理Q?
对于Hibernate Session的生命周期我们采用的是Session-per-Transaction模式Q未采用Open Session in View模式?
虽然Hibernate team认ؓq种Ҏ没什么不好,而且FreeRoller和Atlassian的confluence都用了Open Session In Viewq种模式Q但是我们对它可能生的影响q没有很好的把握Q所以暂时弃|不用?
如果Web层要讉Klazy load的数? 需要先调用Service的业务方? 以获得数? 不过Open Session in View模式其实q不复杂.
问题5QUse case logic ?domain logic 如何区分Q?
Service负责use case logicQdomain model负责domain logic。这L划分看v来很好,实现h很ȝ?BR>如何定什么是use case logicQ什么是domain logicQ?BR>不过如果像第二个问题中最后所? domain model里面没有什么domain logic 的话Q这个问题不存在
问题6QService_度如何定Q?
q个问题真是很烦Q原先考虑使用usecase controller的方式,每个usecase对应一个ServiceQ但是发现这样复用性太低,而且好多地方必须复用相同的功?
另一U方法是用package level serviceQ每个package作个serviceQ这样倒是可以重用Q但是感觉太MQ不好?
回到domain model里面没有什么domain logic的情况下QService的功能就切得很细Q尽量重用,一个package可能有多个service
现在也没有什么很好的办法Q只好在详细设计时根据具体情늡定需要多个Service了。TBD.
问题7Q权限如何设定?如何查?
权限讑֮也是个头疼的问题。我们本x按照use case讑֮权限Q每个用例一个权限。在角色讑֮的时候直接处理的都是业务意义非常明确的权限?BR>但是在权限验证过E中发现了问题:如果在Service的方法中验证权限Q而且q个Ҏ在多个用例中用到Q复用ServiceQ,那么q个Service的方法就需要检查多个权? 如果每个ServiceҎ对应一个权? 那么权限又太l了, 不像use case权限那样代表一个完整的业务.
一U考虑是将权限控制都做在Service上,权限pȝ的设计理忉|自于Unix操作pȝ的权限策略,只不q我把文件和文gҎ成了ServiceQ把l换成了RoleQ此外又加了一些控制对象进去,q样实现下来功能相当的强大,而且很灵z,扩展h很方便,增加一个ServiceQ只需要在配置文g里面讑֮该Service的权限别就行了.
旉对Service的权限是得自己写代码实现的,先是定义一个权限控制接口,然后写了一个抽象类l承该权限接口,把主要的代码实现都写好了Q针对特定的Service不同的部分定义ؓ一个抽象方法,其实该抽象方法也是把类名传递过去而已。这h个要实施权限控制的Service只要l承该抽象方法,然后在配|文仉面定义自q权限U别p了。就以后再增加别的Service模块Q也非常快捷Q不需要动现有的代?BR>如果l合了Spring, 在Spring里面可以使用AOP的办法针对Service增加权限控制Q而不需要自己手工写.
看v来手工方式还是没有上面描q的Spring控制方式优雅
额外的问?
从问?/6中再上升一? 考虑一下DAO的做?一U常见的用法是写一个通用的DAOc? 而是整个pȝ׃个DAO (PersistenceManager), 里面只有create, update, delete entityq?个方法。另外写一个DAO, 专门做查?(QueryManager), 用hibernate的named query做常用的查询Q?用criteria来做Z用户输入的动态查?
criteria ?named query其实都是hardcodeQdomain object attribute name改变的时候,q得记得手工M改这些代码,unit test很难覆盖到所有的criteria search ?named query里面的代?
DAO是ؓ了切换持久层API用的Q准的来说Q是当你在用JDBC来直接访问数据库的时候,׃不同的数据库的sql有差别,所以需要一个DAO来切换不同的数据库的JDBC讉K代码。每个数据库的JDBC代码你都要写一套,当切换数据库的时候,可以通过DAO来切换不同的JDBC代码实现?
是当我们使用Hibernate的时候,除了个别情况Q大部分时候是不需要考虑跨数据库问题的,Hibernate已经做的_好了。个别情况下的代码我们也可以单独做ؓ特例处理Q因此我想不到DAO的必要性了?
如果说用DAO是ؓ了将来切换别的O/R Mapping的话Q我觉得q个话是不现实的Q毕竟每UO/R Mapping的实现方式差异还是很大的Q这直接影响到整个持久层设计.
而当业务层代码是直接调用Hibernate API来操UPO的,持久层就只剩下了单的POJO?
另外一U常见用法是而在业务层针Ҏ个POJOQ写一个对他的CRUD操作的Managerc? 可以参看一下Appfuse的做?其实q等于是在完成DAOImpl应该完成的工作,只不q现在把他归C业务层来说Ş了。然后你可以根据你的业务逻辑来写你的业务Service?BR>在这里,至于如何切换业务逻辑的颗_度问题, ZOOAD的原则,应该把无关的业务攑֜不同的class里面Q降低类之间的耦合性,提高cȝ可重用度。那么根据这个原则如何切分就是一个清楚的事情?
(以上文字整理自洛?QW{N人在爪哇爱死论坛上的讨论)
考虑一?/SPAN>Button来控?/SPAN>Lamp的开和关?/SPAN>
如下的类图,q写下了代码?/FONT>
public class Button {
private Lamp lnkLamp;
public void poll() {
lnkLamp.Turnon();
}
}
但是马上发现q个设计的问题,Buttoncȝ接依赖于Lampc,q个依赖关系意味着?/SPAN>LampcMҎQ?/SPAN>ButtoncM受到影响。此外,想重?/SPAN>ButtoncL控制cM?/SPAN>Lamp的另外一个对象则是不可能的。即Button控制LampQƈ且只能控?/SPAN>Lamp?/SPAN>
昄Q我q反了“高层模块不应该依赖于底层模块,两者都应该依赖于抽象;抽象不应该依赖于具体实现Q细节应该依赖于抽象?/SPAN> q一原则Q?/SPAN>DIP原则Q?/SPAN>
考虑Cq问题,自然地想到应该抽象出一个接口,来消?/SPAN>Button?/SPAN>Lamp的依赖,于是设计如下Q?BR>
q样Q我们倒置?/FONT>Button?/SPAN>Lamp的依赖关p,使得Lamp依赖?/SPAN>SwitchableDevice接口Q?/SPAN>SwitchableDeviceq没有依赖于Buttonc,M知道如何操纵该接口的对象都可以控?/SPAN>Lamp。同?/SPAN>Button不只是可以控?/SPAN>LampQ还可以控制同样实现SwitchableDevice接口的如Computer?/SPAN>Cell Phone{等。回头想惻Iq种做法好像似曾相识Q拍拍脑袋,哦!q不?/SPAN>GoF{略Q?/SPAN>StrategyQ模式吗Q!正是Q不l意间我应用了设计模式Q有点得意哦~~~~Q?/SPAN>
现在再来考虑一个问题,刚才的做法虽然倒置了依赖关p,但是如果?/SPAN>Button作ؓ一个应用程序来控制Lamp或者同样实?/SPAN>SwitchableDevice?/SPAN>Computer?/SPAN>Cell Phone{,则代码可能如下:
public class Button {
private SwitchableDevice lnkLamp;
public Button(){
lnkLamp= new Lamp();
}
?/FONT>
}
也就是说Button?/SPAN>Lamp之间仍然存在?/SPAN>creates》这L依赖关系?/SPAN>
Z解除q种依赖关系Q首先看GoF能作些什么。显Ӟq个地方应该?/SPAN>Factory模式Q将对象的创Zl?/SPAN>Factory Class来处理,q样虽然解开?/SPAN>Lamplg与我们应用程?/SPAN>Button之间的耦合关系Q但是组件的创徏仍然是显式的Q?/SPAN>explicitlyQ,在组件更Ҏ仍需要重新编译?/SPAN>
另外Q通过一?/SPAN>ServiceLocator?/SPAN>look up实现cM是一U解除耦合的办法,看到q儿Q你不禁会想EJB不就是这么实现的嘛,U are Right! Rod Johnson 在其大作J2EE without EJB中称q种方式?/SPAN>Dependency Look upQ但q种方式也有其弊端,比如无法q容器环境Q以及不利于Unit test{?/SPAN>
?/SPAN>Don’t Call us, We will Call you”,q个原则启示我们应该换一个思\Q不应该在应用类中创建具体对象的实例Q而是应该具体对象实例的创徏插入(plug)或者说注射Q?/SPAN>injectQ到应用cMQ这大概是依赖注名U的由来吧?/SPAN>
q种实现方式需要在应用cM及调用组件之间徏立一?/SPAN>assembler来解除两者之间的依赖Q看h与前面的方式没有太大区别Q来看一下结构:
仔细查看会发现还是有比较大的不同Q依赖关pL相反的,也就是说q个q程中依然倒置了依赖关pR?/FONT>Lamp通过Assembler其创徏q程注射CButton中,从而消除了两者之间的耦合Q增加了灉|性?/SPAN>
下面我们看一下具体的实现Q在PicoContainer以及Spring中有着其不同的实现Q分别代表了两种cd?/SPAN>Dependency Injection, ?/SPAN>Constructor Injection ?/SPAN>Setter Injection?/SPAN>
private MutablePicoContainer configureContainer() {
MutablePicoContainer pico = new DefaultPicoContainer();
pico.registerComponentImplementation(SwitchableDevice.class, Lamp.class);
pico.registerComponentImplementation(Button.class);
return pico;
}
然后可以通过MutablePicoContainer?/SPAN>getComponentImplementationҎ获得实现c,调用?/SPAN>pollҎ控制Lamp的开养Iq样一来,两者之间的耦合通过PicoContainer提供?/SPAN>Assembler完全消除了?/SPAN>
Spring则通过一?/SPAN>XML格式的配|文Ӟ两者联pv来,使用Ӟ通过ApplicationContext获得Button BeanQ再调用其方法实玎ͼ同样也消除了耦合关系
了解?/SPAN>IOC模式的思想以及其优点,再来学习其实现。上?/SPAN>blog中大致描qCPicoContainer以及Spring各自?/SPAN>IOC的实玎ͼq篇来详l看一?/SPAN>Spring中它的实现?/SPAN>
Spring?/SPAN>IOC贯穿了其整个框架Q但正如martinflower所_?/SPAN>saying that these lightweight containers are special because they use inversion of control is like saying my car is special because it has wheels”,IOC已经UCؓ框架设计中必不可的部分。就实现上来?/SPAN>Spring采取了配|文件的形式来实C赖的注射Qƈ且支?/SPAN>Type2 IOCQ?/SPAN>Setter InjectionQ以?/SPAN>Type3 IOCQ?/SPAN>Constructor InjectionQ?/SPAN>
Spring?/SPAN>IOC的实现的核心是其Core Bean FactoryQ它框架内部的lg以一定的耦合度组装v来,q对使用它的应用提供一U面向服务的~程模式(SOPQ?/SPAN>Service-Orient Programming)Q比?/SPAN>Spring中的AOP、以及持久化Q?/SPAN>Hibernate?/SPAN>ibaticsQ的实现?/SPAN>
首先从最底层最基础?/SPAN>factory Bean开始,先来?/SPAN>org.springframework.beans.factory.Bean
Factory接口Q它是一个非常简单的接口Q?/SPAN>getBeanҎ是其中最重要的方法,Spring通常是?/SPAN>xml?/SPAN>populate BeanQ所以比较常用的?/SPAN>XMLFactoryBean?BR>
用一个简单的CZ看一下其用法。首先写下两?/FONT>Beanc:
ExampleBean c:
public class ExampleBean {
private String psnName=null;
private RefBean refbean=null;
private String addinfo=null;
public String getAddinfo() {
return getRefbean().getAddress()+getRefbean().getZipcode();
}
public String getPsnName() {
return psnName;
}
public void setPsnName(String psnName) {
this.psnName = psnName;
}
public void setRefbean(RefBean refbean) {
this.refbean = refbean;
}
public RefBean getRefbean() {
return refbean;
}
public void setAddinfo(String addinfo) {
this.addinfo = addinfo;
}
}
RefBeanc:
public class RefBean {
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
private String zipcode=null;
private String address=null;
}
?/FONT>xml配置文g Bean.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="exampleBean" class="test.ExampleBean">
<property name="psnName"><value>xkf</value></property>
<property name="refbean">
<ref bean="refBean"/>
</property>
</bean>
<bean id="refBean" class="test.RefBean">
<property name="address"><value>BeiJing</value></property>
<property name="zipcode"><value>100085</value></property>
</bean>
</beans>
然后可以写个试cL试Q当Ӟ需?/SPAN>Spring中的Spring-core.jar以及commons-logging.jarQ当然在elipse中可以通过安装spring-ide插g来轻村֮现?/SPAN>
public class Test {
public static void main(String[] args){
try{
Resource input = new ClassPathResource("test/Bean.xml");
System.out.println("resource is:"+input);
BeanFactory factory = new XmlBeanFactory(input);
ExampleBean eb =
(ExampleBean)factory.getBean("exampleBean");
System.out.println(eb.getPsnName());
System.out.println(eb.getAddinfo());
}
catch(Exception e){
e.printStackTrace();
}
}
最q项目用Acegi作ؓ安全框架的实?效果不错,写了这文章作为ȝ.
对于M一个完整的应用pȝQ完善的认证和授权机制是必不可少的。在ZSpringFramework的WEB应用中,
我们可以使用Acegi作ؓ安全架构的实现。本文将介绍如何在基于Spring构架的Web应用中用AcegiQƈ且详l介
l如何配|和扩展Acegi框架以适应实际需要?BR>
文章和代码下?
http://www.tkk7.com/Files/youlq/Acegi.zip
注意Q许多朋友在部v上遇C些麻烦,所以我可以部|的完整的war文g传上来,注意Qjava代码在acegi-sample.war\WEB-INF 目录下,例子需要MysqlQ徏库脚本在acegi-sample.war\db目录下?/FONT>
acegi-sample.part1.rar
acegi-sample.part2.rar
acegi-sample.part3.rar
acegi-sample.part4.rar
附注Q?BR>
有些朋友询问我如何部|文中的例子Q在此再ơ说明一下(文章中已l有提到Q:
Mysql的徏表脚本在db目录?BR>Z减小体积Q已l将WEB-INF\lib下的依赖包删除,误行下载以下包Qƈ拯至WEB-INF\lib下:
spring-1.2.4.jar
acegi-security-0.8.3.jar
aopalliance-1.0.jar
c3p0-0.9.0.jar
commons-logging-1.0.4.jar
ehcache-1.1.jar
log4j-1.2.8.jar
mysql-connector-java-3.1.10-bin.jar
oro-2.0.8.jar
提示Q?BR>acegi-security-0.8.3.jar
aopalliance-1.0.jar
c3p0-0.9.0.jar
commons-logging-1.0.4.jar
ehcache-1.1.jar
log4j-1.2.8.jar
oro-2.0.8.jar
可以在acegi-security-0.8.3.zip所带的acegi-security-sample-contacts-filter.war中找到?BR>spring-1.2.4.jar
mysql-connector-java-3.1.10-bin.jar
要分别到springframework和mysql|站下蝲?/P>
Acegi安全pȝQ是一个用于Spring Framework的安全框Ӟ能够和目前流行的Web容器无缝集成。它使用了Spring的方式提供了安全和认证安全服务,包括使用Bean ContextQ拦截器和面向接口的~程方式。因此,Acegi安全pȝ能够L地适用于复杂的安全需求?
安全涉及C个不同的概念Q认证和授权。前者是关于认用户是否实是他们所宣称的n份。授权则是关于确认用h否有允许执行一个特定的操作?
在Acegi安全pȝ中,需要被认证的用Ppȝ或代理称?Principal"。Acegi安全pȝ和其他的安全pȝ不同Q它q没有角色和用户l的概念?
Acegi安全pȝ包含以下七个关键的功能组Ӟ
l Authentication对象Q包含了PrincipalQCredential和Principal的授权信息。同时还可以包含关于发v认证h的客L其他信息Q如IP地址?
2 ContextHolder对象Q用ThreadLocal储存Authentication对象的地斏V?
3 AuthenticationManagerQ用于认证ContextHolder中的Authentication对象?
4 AccessDecissionManagerQ用于授权一个特定的操作?
5 RunAsManagerQ当执行特定的操作时Q用于选择性地替换Authentication对象?
6 Secure Object拦截器,用于协调AuthenticationManagerQAccessDecissionManagerQRunAsManager和特定操作的执行?
7 ObjectDefinitionSourceQ包含了特定操作的授权定义?
q七个关键的功能lg的关pd下图所C(图中灰色部分是关键组ӞQ?/P>
Acegi安全pȝ目前支持两类安全理对象?
W一cȝ安全理对象理AOP Alliance的MethodInvocationQ开发h员可以用它来保护Spring容器中的业务对象。ؓ了Spring理的Bean可以作ؓMethodInvocation来用,Bean可以通过ProxyFactoryBean和BeanNameAutoProxyCreator来管理,像在Spring的事务管理一样用?
W二cLFilterInvocation。它用过滤器QFilterQ来创徏Qƈ单地包装了HTTP的ServletRequestQServletResponse和FilterChain。FilterInvocation可以用来保护HTTP资源。通常Q开发h员ƈ不需要了解它的工作机Ӟ因ؓ他们只需要将Filter加入web.xmlQAcegi安全pȝ可以工作了?
每个安全理对象都可以描q数量不限的各种安全认证h。例如,MethodInvocation对象可以描述带有L参数的Q意方法的调用Q而FilterInvocation可以描述L的HTTP URL?
Acegi安全pȝ需要记录应用于每个认证h的安全配|参数。例如,对于BankManager.getBalanceQint accountNumberQ方法和BankManager.approveLoanQint applicationNumberQ方法,它们需要的认证h的安全配|很不相同?
Z保存不同的认证请求的安全配置Q需要用配|参数。从实现的视角来看,配置参数使用ConfigAttribute接口来表C。Acegi安全pȝ提供了ConfigAttribute接口的一个实玎ͼSecurityConfigQ它把配|参C存ؓ一个字W串?
ConfigAttributeDefinitioncLConfigAttribute对象的一个简单的容器Q它保存了和特定h相关的ConfigAttribute的集合?
当安全拦截器收到一个安全认证请求时Q需要决定应用哪一个配|参数。换句话_它需要找出应用于q个h的ConfigAttributeDefinition对象。这个查扄q程是由ObjectDefinitionSource接口来处理的。这个接口的主要Ҏ是public ConfigAttributeDefinition getAttributes(Object object)Q其中Object参数是一个安全管理对象。因为安全管理对象包含有认证h的详l信息,所以ObjectDefinitionSource接口的实现类可以从中获得所需的详l信息,以查扄关的ConfigAttributeDefiniton对象?
Z说明Acegi安全pȝ如何工作Q我们设想一个用Acegi的例子。通常Q一个安全系l需要发挥作用,它必d成以下的工作Q?
l 首先Q系l从客户端请求中获得Principal和CredentialQ?
2 然后pȝ认证Principal和Credential信息Q?
3 如果认证通过Q系l取出Principal的授权信息;
4 接下来,客户端发h作请求;
5 pȝҎ预先配置的参数检查Principal对于该操作的授权Q?
6 如果授权查通过则执行操作,否则拒绝?
那么QAcegi安全pȝ是如何完成这些工作的呢?首先Q我们来看看Acegi安全pȝ的认证和授权的相关类图:
图中l色部分是安全拦截器的抽象基c,它包含有两个理c,AuthenticationManager和AccessDecisionManagerQ如图中灰色部分。AuthenticationManager用于认证ContextHolder中的Authentication对象Q包含了PrincipalQCredential和Principal的授权信息)QAccessDecissionManager则用于授权一个特定的操作?
下面来看一个MethodSecurityInterceptor的例子:
<bean id="bankManagerSecurity"
class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor">
<property name="validateConfigAttributes">
<value>true</value>
</property>
<property name="authenticationManager">
<ref bean="authenticationManager"/>
</property>
<property name="accessDecisionManager">
<ref bean="accessDecisionManager"/>
</property>
<property name="objectDefinitionSource">
<value>
net.sf.acegisecurity.context.BankManager.delete*=
ROLE_SUPERVISOR,RUN_AS_SERVER
net.sf.acegisecurity.context.BankManager.getBalance=
ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_
</value>
</property>
</bean>
上面的配|文件中QMethodSecurityInterceptor是AbstractSecurityInterceptor的一个实现类。它包含了两个管理器QauthenticationManager和accessDecisionManager。这两者的配置如下Q?/P>
<bean id="authenticationDao" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<bean id="daoAuthenticationProvider"
class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref bean="authenticationDao"/></property>
</bean>
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list><ref bean="daoAuthenticationProvider"/></list>
</property>
</bean>
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list><ref bean="roleVoter"/></list>
</property>
</bean>
准备工作做好了,现在我们来看看Acegi安全pȝ是如何实现认证和授权机制的。以使用HTTP BASIC认证的应用ؓ例子Q它包括下面的步骤:
1. 用户dpȝQAcegi从acegisecurity.ui子系l的安全拦截器(如BasicProcessingFilterQ中得到用户的登录信息(包括Principal和CredentialQƈ攑օAuthentication对象Qƈ保存在ContextHolder对象中;
2. 安全拦截器将Authentication对象交给AuthenticationManagerq行w䆾认证Q如果认证通过Q返回带有Principal授权信息的Authentication对象。此时ContextHolder对象的Authentication对象已拥有Principal的详l信息;
3. 用户d成功后,l箋q行业务操作Q?
4. 安全拦截器(bankManagerSecurityQ收到客L操作h后,操作请求的数据包装成安全管理对象(FilterInvocation或MethodInvocation对象Q;
5. 然后Q从配置文gQObjectDefinitionSourceQ中d相关的安全配|参数ConfigAttributeDefinitionQ?
6. 接着Q安全拦截器取出ContextHolder中的Authentication对象Q把它传递给AuthenticationManagerq行w䆾认证Qƈ用返回值更新ContextHolder的Authentication对象Q?
7. Authentication对象QConfigAttributeDefinition对象和安全管理对象(secure ObjectQ交lAccessDecisionManagerQ检查Principal的操作授权;
8. 如果授权查通过则执行客Lh的操作,否则拒绝Q?
AccessDecisionVoter
注意上节的accessDecisionManager是一个AffirmativeBasedc,它对于用h权的投票{略是,只要通过其中的一个授权投检查,卛_通过Q它的allowIfAllAbstainDecisions属性值是falseQ意思是如果所有的授权投票是都是弃权,则通不q授权检查?
Acegi安全pȝ包括了几个基于投策略的AccessDecisionManagerQ上节的RoleVoter是其中的一个投策略实玎ͼ它是AccessDecisionVoter的一个子cRAccessDecisionVoter的具体实现类通过投票来进行授权决{,AccessDecisionManager则根据投结果来军_是通过授权查,q是抛出AccessDeniedException例外?
AccessDecisionVoter接口共有三个ҎQ?
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config);
public boolean supports(ConfigAttribute attribute);
public boolean supports(Class clazz);
其中的voteҎq回intq回|它们是AccessDecisionVoter的三个静态成员属性:ACCESS_ABSTAIN,QACCESS_DENIED和ACCESS_GRANTEDQ它们分别是弃权Q否军_赞成?
Acegi安全pȝ中,使用投票{略的AccessDecisionManager共有三个具体实现c:AffirmativeBased、ConsensusBased和UnanimousBased。它们的投票{略是,AffirmativeBasedcd需有一个投赞成即可通过QConsensusBasedc需要大多数投票赞成卛_通过Q而UnanimousBasedc需要所有的投票赞成才能通过?
RoleVotercL一个Acegi安全pȝAccessDecisionVoter接口的实现。如果ConfigAttribute以ROLE_开_RoleVoter则进行投。如果GrantedAuthority的getAutorityҎ的Stringq回值匹配一个或多个以ROLE_开头的ConfigAttributeQ则投票通过Q否则不通过。如果没有以ROLE_开头的ConfigAttributeQRoleVoter则弃权?
q两天根据夏昕的Spring开发指南,在加上自己参考其他资料,做出了一个利用Spring的MVCQ加上Hibernate的网站的实例Q包括Jsp界面表单的提交,Spring?MVC控制Q用Spring提供的HibernateTemplateq行数据持久的完整例?
下面把程序的详细代码贴出来,׃自己也是初学Q有不对的地方,q请q\的大侠指点一二?/P>
一、需求:表单填写用户IDQ用户名和地址Q写入数据库
二、具体实现。。。?nbsp; 代码见详l内?/P>
|上讲Spring的资料很多,大部分实例都是针对Spring的某一部分做出的例子,如Ioc,Aop,MVC,数据持久层等{,没有一个完整的例子?/P>
q两天根据夏昕的Spring开发指南,在加上自己参考其他资料,做出了一个利用Spring的MVCQ加上Hibernate的网站的实例Q包括Jsp界面表单的提交,Spring?MVC控制Q用Spring提供的HibernateTemplateq行数据持久的完整例?
下面把程序的详细代码贴出来,自己也是初学Q有不对的地方,q请q\的大侠指点一二?/P>
一、需求:表单填写用户IDQ用户名和地址Q写入数据库
二、具体实?/P>
1.UserInfoAdd.jsp
填写表单界面Q输入ID,Name,Address后提交到UserInfoAddAction.do
UserID:Name: Address: |
2.UserInfoVO
表单的映?Value Objectc,用来获取表单提交的数?/P>
package com.kacakong.spring.vo; public class UserInfoVO { public void setAddress(String address) { public String getName() { public void setName(String name) { public String getNid() { public void setNid(String nid) { } |
3.UserInfoAddAction
Controlc,ҎSpring的配|文Ӟ调用合适的接口Q执行业务操作,控制面转向。如果添加数据成功,重定向到成功页面,如果p|Q就装到操作p|面。具体调用的操作c,是根据Spring的配|文件来扄。用的是接口操作,而不是实现类的操作?/P>
package com.kacakong.spring.action; import org.springframework.validation.BindException; import com.kacakong.hibernate.dao.UserInfoDAO; public class UserInfoAddAction extends SimpleFormController { private String success_view; private int doAdd(UserInfo userinfo) { public String getSuccess_view() { public void setFail_view(String string) { public void setSuccess_view(String string) { } |
4.UserInfo
POc,数据model层的反映Q与数据库徏立O/R mapping的映?/P>
package com.kacakong.hibernate.pojos; public class UserInfo { public Integer getNid() { public void setNid(Integer nid) {
|
5.UserInfo.hbm.xml
PO的hibernate O/R mapping定义文gQ这里的ID我设|成了assignedE序获取
6.UserInfoDAO
接口c,只定义了实现Ҏ名称
package com.kacakong.hibernate.dao; import com.kacakong.hibernate.pojos.UserInfo; public interface UserInfoDAO { |
7.UserInfoDAOImp
UserInfoDAO接口的实玎ͼl承了HibernateDaoSupport
package com.kacakong.hibernate.dao;
import org.springframework.orm.hibernate.support.HibernateDaoSupport; import com.kacakong.hibernate.pojos.UserInfo; public class UserInfoDAOImp extends HibernateDaoSupport implements UserInfoDAO { public UserInfo get(Integer id) { public void save(UserInfo userinfo) { } |
8.Web.xml的配|?/P>
|
9.Config.xml的定?/P>
10.Hibernate-Context.xml的定?/P>
下面是要引入的JAR?BR> <classpathentry kind="lib" path="D:/classes/spring.jar"/>
<classpathentry kind="lib" path="D:/classes/junit-3.8.1.jar"/>
<classpathentry kind="lib" path="D:/classes/spring-dao.jar"/>
<classpathentry kind="lib" path="D:/classes/mysql-connector-java-3.0.9-stable-bin.jar"/>
<classpathentry kind="lib" path="D:/classes/commons-dbcp-1.1.jar"/>
<classpathentry kind="lib" path="D:/classes/commons-pool-1.1.jar"/>
<classpathentry kind="lib" path="D:/classes/commons-collections.jar"/>
<classpathentry kind="lib" path="D:/classes/commons-beanutils.jar"/>
<classpathentry kind="lib" path="D:/classes/commons-lang-2.0.jar"/>
<classpathentry kind="lib" path="D:/classes/commons-logging-1.0.3.jar"/>
SpringDao.java文g
package com.bcxy.spring.dao;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowCallbackHandler;
import com.bcxy.spring.dao.model.TestModel;
public class SpringDao {
DataSource ds = null;
JdbcTemplate jt = null;
Log log = LogFactory.getLog(SpringDao.class);
public List springQuery(){
final List tests = new ArrayList();
String sql = " select * from test ";
jt = new JdbcTemplate(ds);
jt.query(sql, new RowCallbackHandler(){
public void processRow(ResultSet rs) throws SQLException {
//
TestModel tm = new TestModel();
tm.setId(rs.getInt("id"));
tm.setPw(rs.getString("pw"));
tm.setUn(rs.getString("un"));
//
tests.add(tm);
}
});
return tests;
}
public void springUpdate() {
String sql = "update test set pw=? where id=?";
jt = new JdbcTemplate(ds);
jt.update(sql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
//
ps.setString(1, "maxcard");
ps.setInt(2, 1);
}
});
log.info("update test a record.");
}
/**
* @return
*/
public DataSource getDs() {
return ds;
}
/**
* @param source
*/
public void setDs(DataSource source) {
ds = source;
}
}
Spring配置文g
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "<beans>
<description>Spring Quick Start</description>
<!-- datasource -->
<bean id="datasource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/test</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
<!-- test dao -->
<bean id="testdao"
class="com.bcxy.spring.dao.SpringDao"
>
<property name="ds">
<ref local="datasource"/>
</property>
</bean>
</beans>
下面我们用JUnit试一?
......
public void testSpringUpdate() throws FileNotFoundException {
//
InputStream is = new FileInputStream("bean.xml");
XmlBeanFactory bean = new XmlBeanFactory(is);
SpringDao sd = (SpringDao)bean.getBean("testdao");
sd.springUpdate();
}