Spring入门 | |
Spring是一个非怼U的轻量框架Q通过Spring的IoC容器Q我们的x点便攑ֈ了需要实现的业务逻辑上。对AOP的支持则能让我们动态增Z务方法。编写普通的业务逻辑Bean是非常容易而且易于试的,因ؓ它能qJ2EE容器Q如ServletQJSP环境Q单独进行单元测试。最后的一步便是在Spring框架中将q些业务Bean以XML配置文g的方式组lv来,它们按照我们预定的目标正常工作了!非常ҎQ?/p> 本文给Z个基本的Spring入门CZQƈ演示如何使用Spring的AOP复杂的业务逻辑分离到每个方面中?/p> 1Q开发环境配|?br />2Q编写Bean接口及其实现 1Q开发环境配|?/h3>首先Q需要正配|Java环境。推荐安装JDK1.4.2Qƈ正确配置环境变量Q?/p> JAVA_HOME=<JDK安装目录> 我们用免费的Eclipse 3.1作ؓIDE。新Z个Java ProjectQ将Spring的发布包spring.jar以及commons-logging-1.0.4.jar复制到Project目录下,q在Project > Properties中配|好Java Build PathQ?/p> 2Q编写Bean接口及其实现我们实现一个管理用L业务Bean。首先定义一个ServiceBean接口Q声明一些业务方法: /** /** 然后在MyServiceBean中实现接口: /** import java.util.*; public class MyServiceBean implements ServiceBean { private String dir; public void setUserDir(String dir) { public void addUser(String username, String password) { public void deleteUser(String username) { public boolean findUser(String username) { public String getPassword(String username) { Z化逻辑Q我们用一个Map保存用户名和口o?/p> 现在Q我们已l有了一个业务Bean。要试它非常容易,因ؓ到目前ؓ止,我们q没有涉及到Spring容器Q也没有涉及CQ何Web容器Q假定这是一个Web应用E序关于用户理的业务BeanQ。完全可以直接进行Unit试Q或者,单地写个mainҎ试Q?/p> /** public class Main { public static void main(String[] args) throws Exception { 执行l果Q?br /> 3Q在Spring中配|Beanq获得Bean的实?/h3>我们已经在一个mainҎ中实C业务Q不q,对象的生命周期交给容器理是更好的办法Q我们就不必为初始化对象和销毁对象进行硬~码Q从而获得更大的灉|性和可测试性?/p> 惌把ServiceBean交给Spring来管理,我们需要一个XML配置文g。新Z个beans.xmlQ放到src目录下,保在classpath中能扑ֈ此配|文Ӟ输入以下内容Q?/p> <?xml version=1.0 encoding=UTF-8?> 以上XML声明了一个id为service的BeanQ默认地QSpring为每个声明的Bean仅创Z个实例,q过id来引用这个Bean。下面,我们修改mainҎQ让Spring来管理业务BeanQ?/p> /** import org.springframework.beans.factory.xml.XmlBeanFactory; public class Main { public static void main(String[] args) throws Exception { 执行l果Q?br /> ׃我们要通过mainҎ启动Spring环境Q因此,首先需要初始化一个BeanFactory。红色部分是初始化Spring的BeanFactory的典型代码,只需要保证beans.xml文g位于classpath中?/p> 然后Q在BeanFactory中通过id查找Q即可获得相应的Bean的实例,q将光当转型为合适的接口?/p> 接着Q实Cpd业务操作Q在应用E序l束前,让Spring销毁所有的Bean实例?/p> Ҏ上一个版本的MainQ可以看出,最大的变化是不需要自q理Bean的生命周期。另一个好处是在不更改实现cȝ前提下,动态地为应用程序增加功能?/p> 4Q编写Advisor以增强ServiceBean所谓AOPx分散在各个Ҏ处的公共代码提取C处,q过cM拦截器的机制实现代码的动态织入。可以简单地惌成,在某个方法的调用前、返回前、调用后和抛出异常时Q动态插入自q代码。在弄清楚Pointcut、Advice之类的术语前Q不如编写一个最单的AOP应用来体验一下?/p> 考虑一下通常的Web应用E序都会有日志记录,我们来编写一个LogAdvisorQ对每个业务Ҏ调用前都作一个记录: /** import java.lang.reflect.Method; public class LogAdvisor implements MethodBeforeAdvice { 然后Q修改beans.xmlQ?/p> <?xml version=1.0 encoding=UTF-8?> <beans> <bean id="logAdvisor" class="com.crackj2ee.example.spring.LogAdvisor" /> <bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> 注意观察修改后的配置文gQ我们用了一个ProxyFactoryBean作ؓservice来与客户端打交道Q而真正的业务Bean即MyServiceBean被声明ؓserviceTargetq作为参数对象传递给ProxyFactoryBeanQproxyInterfaces指定了返回的接口cd。对于客L而言Q将感觉不出M变化Q但却动态加入了LogAdvisorQ关pd下: q行l果如下Q可以很Ҏ看到调用了哪些方法: 要截h定的某些Ҏ也是可以的。下面的例子修改getPassword()Ҏ的返回| /** import org.aopalliance.intercept.MethodInterceptor; public class PasswordAdvisor implements MethodInterceptor { q个PasswordAdvisor截获ServiceBean的getPassword()Ҏ的返回|q将其改?**。l修改beans.xmlQ?/p> <?xml version=1.0 encoding=UTF-8?> <bean id="logAdvisor" class="com.crackj2ee.example.spring.LogAdvisor" /> <bean id="passwordAdvisorTarget" class="com.crackj2ee.example.spring.PasswordAdvisor" /> <bean id="passwordAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> 利用Spring提供的一个RegexMethodPointcutAdvisor可以非常Ҏ地指定要截获的方法。运行结果如下,可以看到q回l果变ؓ******Q?br /> q需要l增强ServiceBeanQ我们编写一个ExceptionAdvisorQ在业务Ҏ抛出异常时能做一些处理: /** import org.springframework.aop.ThrowsAdvice; public class ExceptionAdvisor implements ThrowsAdvice { 此Adviced到beans.xml中,然后在业务Bean中删除一个不存在的用P故意抛出异常Q?/p> service.deleteUser("not-exist"); 再次q行Q注意到ExceptionAdvisor记录下了异常Q?br /> 5Qȝ利用Spring非常强大的IoC容器和AOP功能Q我们能实现非常灉|的应用,让Spring容器理业务对象的生命周期,利用AOP增强功能Q却不媄响业务接口,从而避免更改客L代码?/p> Z实现q一目标Q必dl牢讎ͼ面向接口~程。而Spring默认的AOP代理也是通过Java的代理接口实现的。虽然Spring也可以用CGLIB实现Ҏ通类的代理,但是Q业务对象只要没有接口,׃变得难以扩展、维护和试?/p> |
Learn Spring in spring(? | |
Bromon原创 请尊重版?br /> Spring和EJB一P提供了两U事务管理方式:~程式和声明式。在考试pȝ中我们将使用声明式的事务理Q这是spring推荐的做法。用这U方式可以体验到spring的强大便P而且我们无须在DaocM~写MҎ的代码,只需要通过配置文g可以让普通的javacd载到事务理中,q个意义是很重大的?br /> Spring中进行事务管理的通常方式是利用AOPQ面向切片编E)的方式,为普通javacd装事务控Ӟ它是通过动态代理实现的Q由于接口是延迟实例化的Qspring在这D|间内通过拦截器,加蝲事务切片。原理就是这P具体l节请参考jdk中有兛_态代理的文档。本文主要讲解如何在spring中进行事务控制?br /> 动态代理的一个重要特征是Q它是针Ҏ口的Q所以我们的dao要通过动态代理来让spring接管事务Q就必须在dao前面抽象Z个接口,当然如果没有q样的接口,那么spring会用CGLIB来解决问题,但这不是spring推荐的方式,我们也不做讨论?br /> 参照前面的例子,我们为StudentManager.java定义一个接口,它的内容如下Q?br />
StudentManager也应该做Z改,实现该接口:
现在需要修攚w|文Ӟ用于定义Hibrenate适用的事务管理器Qƈ且把sessionFactory注入q去Q同时还需要通过注册一个DefaultTransactionAttribute对象Q来指出事务{略。其中sessionFactory的定义已l在本文的第三章中说明?br /> 首先定义一个Hibernate的事务管理器Q让它来理sessionFactoryQ?br />
下面定义事务理{略Q我们希望把{略定义在方法这个别上Q提供最大的灉|性,本例中将addҎ定义为:PROPAGATION_REQUIRES_NEWQ这可以保证它将始终q行在一个事务中?br />
我们不仅可以为addҎ定义事务{略,q可以定义事务隔ȝ度和回滚{略,他们以逗号隔开,比如我们的add事务可以定义?
q个事务{略表示addҎ会独占一个事务,当事务过E中产生ExamerException异常Q事务会回滚?br /> Add/update/del都是写入ҎQ对于selectQ读取)ҎQ我们可以指定较为复杂的事务{略Q比如对于loadAll()ҎQ?br />
该事务的含义为,loadAllҎ支持事务Q不会读取未提交的数据,它的数据为只读(可提高执行速度Q?br /> 如你所见,我们的StudentManagerInterface接口中还有一个loadById(int id)ҎQ也许我们将来还会有很多的loadByXXXX的方法,N要一一Z们指定事务策略?太烦ZQ他们应该和loadAll()一P所以我们可以用通配W,定义所有的loadXXXXҎQ?br />
现在可以定义事务理器:
q个bean的外观是一个接?StudentManagerInterface)Q我们指Z它的具体实现(studentManager)Q而且为它l定了事务策略。在客户端用的时候,获得对象是StudentManagerInterfaceQ所有的操作都是针对q个接口的。测试代码ƈ没有改变Q我们虽然修改了很多地方Q加入了事务控制Q但是客Lq没有受到媄响,q也体现了spring的一些优ѝ测试代码如下:
通过以上的代码可以看出,spring可以单的把普通的java classU_事务理Q声明性的事务操作h也很Ҏ。有了spring之后Q声明性事务不再是EJB独有Q我们不必ؓ了获得声明性事务的功能而去忍受EJB带来的种U不ѝ?br /> 我所使用的mysql是不支持事务的,你可以更换用PostgreSQLQ有了spring+hibernateQ更换dbq不像以前那h怖了Q步骤很单: 1、 添加PostgreSQL的jdbc驱动 2、 修改dataSource配置Q包括驱动名U、url、帐受密?br />3、 修改sessionFactory的数据库dailet为net.sf.hibernate.dialect.PostgreSQLDialect 4、 修改hbm.xml中的主键生成{略为increment 所有的修改都在配置文g中完成,业务代码不需要Q何修改,我很满意QHow about u? 附A pring中的所有事务策?br /> PROPAGATION_MANDATORY PROPAGATION_NESTED PROPAGATION_NEVER PROPAGATION_NOT_SUPPORTED PROPAGATION_REQUIRED PROPAGATION_REQUIRED_NEW PROPAGATION_SUPPORTS 附B Spring中所有的隔离{略Q?br /> ISOLATION_DEFAULT ISOLATION_READ_UNCOMMITED ISOLATION_COMMITED ISOLATION_REPEATABLE_READ ISOLATION_SERIALIZABLE |
Learn Spring in spring(? | |
Bromon原创 请尊重版?br /> spring中对hibernate的支持是非常强大的,从一个简单的例子q得出来,从这个例子中我们q将Ҏ谓的轻量U容器做一些讨论?br /> 首先需要配|数据源Q通常我们有两U方式获得ConnectionQ一是自q写代码获得连接,二是从JNDI环境中得到DataSourceQ然后生一个Connection。无论怎样Q既然是spring下面的对象,应该注册到配置文g中。假设我们需要一个连接mysql下面一个叫做examer的数据库Q手动方式的配置是:
很好L不是Q假如我们用JNDI数据源,那么dataSource的声明就应该是:
你需要在JNDI环境中绑定一个名为jdbc/springExamer的东西,q段代码才有实际意义。另外需要提醒的是,所有的bean声明Q它的id必须是唯一的?br /> 在本pȝ中,数据库操作是被hibernate装h的,所以dataSource是不需要注入到具体的逻辑cMQ它只会被注lhibernate的sessionFactory?br /> 按照常规思\Q我们需要在spring中注册hibernate的sessionFactoryQ它应该是我们自q写的一个类Q获得dataSourceQ返回sessionFactoryQ其他的逻辑c通过q个sessionFactory获得sessionq行数据库操作?br /> 但是我们有另外一U选择Qspring直接提供了对sessionFactory的封装,你只需要注册一个spring自己的类Q给它提供必ȝ属性,它会q回一个org.springframework.orm.hibernate.HibernateTemplateQ这个类装了add、del{操作,它的装E度相当高,通过它来~写hibernate应用非常单。但是问题出来了Q我们该如何选择Q?br /> 表面上看Q用spring自己的库无疑更加单,但是h意,spring是一个轻量的框Ӟ所谓轻量Q一个重要特征就是无侵入性,也就是你使用q套框架Q不会被它绑定,被spring理的类Q应该不需要用它的接口和抽象c,q样你的pȝ不会对spring产生依赖。但是如果你使用了spring装的方式去操作hibernateQ就必须l承org.springframework.orm.hibernate.support.HibernateDaoSupportc,q导致了l定。所以做q样的选择是有点痛苦的Q如果有一天spring框架不存在了Q你的代码怎么升l护Q具体问题只能具体分析,在我们的应用中,完全使用了spring装的HibernateTemplateQ它太好用了Q所以容易上瘾?br /> 假设我们有一张student表,l构很简单:
设计一个StudentcL映射q张表: ~写Student.hbm.xmlQ让hibernate知道如何d联student表和Studentc,该文件和Student.java在同一目录Q?br />
然后我们可以在spring中配|sessionFactoryQ?br />
其中引用了我们之前注册过的dataSourceQmappingDirectoryLocations属性指明了.hbm.xml文g在哪里\径,该文件夹下面?hbm.xml文g会被全部加蝲?br /> 一切都准备qAQ现在我们要加入一个StudentManagerc,来进行增删查改的操作Q?br />
该类只演CZ如何增加一个StudentQHibernateTemplateq封装了很多有用的方法,h阅spring文档。StudentManager中的sessionFactory是由spring注入的,但是StudentManagerq没有对sessionFactory做Q何的处理Q这是因为所有的处理都被HibernateDaoSupport.getHibernateTemplate()装。整个StudentManager中也看不CQ何的异常处理Q他们也都被基类装了?br /> 最后一个步骤就是在spring中注册StudentMangerQ然后向它注入sessionFactoryQ?br />
所有的配置都完成了Q下面做单元试Q?br />
Spring已经hibernate的操作简化到了非帔R的程度,最关键的是整个开发可以由设计来驱动,如果一个团队对spring有够的熟悉Q那么完全可以由设计师规划所有的c,整理清楚cM间的关系Q写成配|文Ӟ然后~写hibernate映射文gQ将数据表与pojo兌Q成员就可以完全在设计方案内工作Q利用spring装好的hibernate模版Q开发v来速度非常快,调试也很Ҏ。它能够解决如何在团队内贯彻设计Ҏ的问题?br /> ׃本文不讲解hibernate的用,所以相兛_容请查阅hibernate文档?br /> 下一:spring中的事务控制 |
Learn Spring in spring(? | |
Bromon原创 请尊重版?br /> M需要交lspring理的对象,都必d配置文g中注册,q个q程被称为wiringQ下面做一个最单的Hello world演示Q我们将要注册的cd下:
然后我们来编写一个spring配置文gQ文件名LQ在我这里它是springConfig.xmlQ需要注意的是这个文件应该存攑֜classpath所包含的\径中Q?br />
通过使用bean标签Q注册了一个HelloTalker对象Q它的名字叫做helloTalker。然后我们编写一个测试类Q它的工作是利用spring框架提供的接口,加蝲配置文gQ通过指定对象的idQ获得一个对象。它的代码如下:
q个E序完成了Q因为只有一个对象HelloTalker被注册到了spring中,所以不存在对象间的依赖Q当然也׃涉及依赖注入。下面演CZ个简单的依赖注入Q?br /> W一步是修改HelloTalkerQ增加一个String name属性:
属性编写setҎQ该Ҏ必须严格遵守javabean的命名规则:
修改greetingҎQ?br />
如你所见,name属性没有初试化Q因为它的值将在运行过E中被spring动态注入?br /> W二步,修改springConfig.xml中唯一的这个bean配置Q?br />
修改完成。我们将一个名字”bromon”写dspringConfig.xml中,它会被动态的注入到HelloTalker的name属性中QgreetingҎ会把它打印出来。重新运行刚才的junitc,可以看到l果?br /> 我们只演CZ如何注入一个最单的StringQ实际上我们可以注入M值类型,也可以注入Q何类的实例,也可以注入List、Map、Properties。配|文件管理了所有的对象和对象间的关p,而对象则只负责执行自q功能Q他们的职责少Q藕合度低Q系l就容易测试,理l护也更Ҏ?br /> <bean>标签q有很多属性,用于指定对象如何被实例化Q它也有很多子标{于配|对象的属性,请大家参考相关的DTD和文档,能够很快的掌握。本pd文章不是spring手册Qspring的基知识请参考spring in actionQ够详l准。后面的章节更多的讨论系l设计、开发的一些细节和高Ҏ?br /> 下一:在spring中进行hibernate开?br /> |