《第5章JavaEE基础应用教程之Spring应用.ppt》由会员分享,可在线阅读,更多相关《第5章JavaEE基础应用教程之Spring应用.ppt(65页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第5章 Spring应用5.1 Spring概述概述5.2 Spring核心机制核心机制依赖注入依赖注入5.3 Spring核心接口及基本配置核心接口及基本配置5.4 Spring AOP5.5 Spring事务支持事务支持5.6 Spring与与Struts 2整合应用整合应用5.7 Spring与与Hibernate整合应用整合应用5.1 Spring概述Spring框架的主要优势之一是其分层架构,分层架构允许选择使用任一个组件,同时为 Java EE 应用程序开发提供集成的框架。Spring 框架的分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义
2、了创建、配置和管理 Bean 的方式,如图5.1所示。图5.1Spring框架的组件结构图5.1 Spring概述组成Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。各模块的功能如下:核心容器。提供Spring框架的基本功能,其主要组件是BeanFactory,是工厂模式的实现。Spring上下文。向Spring框架提供上下文信息,包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度等。SpringAOP。通过配置管理特性,可以很容易地使Spring框架管理的任何对象支持AOP。SpringAOP模块直接将面向方面编程的功能集成到Spring框架
3、中。它为基于Spring应用程序的对象提供了事务管理服务。SpringDAO。JDBCDAO抽象层提供了有用的异常层次结构,用来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(如打开和关闭连接)。5.1 Spring概述 Spring ORM。Spring 框架插入了若干ORM框架,提供ORM的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map,并且都遵从Spring 的通用事务和 DAO 异常层次结构。Spring Web 模块。为基于 Web 的应用程序提供上下文。它建立在应用程序上下文模块之上,
4、简化了处理多份请求及将请求参数绑定到域对象的工作。Spring 框架支持与 Jakarta Struts 的集成。Spring MVC 框架,是一个全功能构建Web应用程序的 MVC 实现。通过策略接口实现高度可配置,MVC 容纳了大量视图技术,其中包括JSP、Velocity、Tiles、iText和POI。5.2 Spring核心机制依赖注入5.2.1 工厂模式工厂模式下面举例说明工厂模式的应用。创建一个Java Project,命名为“FactoryExample”。在src文件夹下建立包face,在该包下建立接口Human,代码如下:package face;public interf
5、ace Human void eat();void walk();在src文件夹下建立包iface,在该包下建立Chinese类和American类,分别实现Human接口。Chinese.java代码如下:package iface;import face.Human;public class Chinese implements Humanpublic void eat()System.out.println(中国人很会吃!);public void walk()System.out.println(中国人健步如飞!);5.2.1 工厂模式American.java代码如下:package
6、 iface;import face.Human;public class American implements Humanpublic void eat()System.out.println(美国人吃西餐!);public void walk()System.out.println(美国人经常坐车!);5.2.1 工厂模式在src文件夹下建包factory,在该包内建立工厂类Factory,代码如下:package factory;import iface.American;import iface.Chinese;import face.Human;public class Facto
7、ry public Human getHuman(String name)if(name.equals(Chinese)return new Chinese();else if(name.equals(American)return new American();elsethrow new IllegalArgumentException(参数不正确);5.2.1 工厂模式在src文件夹下建包test,在该包内建立测试类Test,代码如下:packagetest;importface.Human;importfactory.Factory;publicclassTestpublicstatic
8、voidmain(Stringargs)Humanhuman=null;human=newFactory().getHuman(Chinese);human.eat();human.walk();human=newFactory().getHuman(American);human.eat();human.walk();5.2.1 工厂模式该程序为Java应用程序,直接运行可看出结果,如图5.2所示。图5.2工厂模式运行结果5.2.2 依赖注入应用1.为项目添加为项目添加Spring开发能力开发能力右击项目名,选择【MyEclipse】【AddSpringCapabilities】菜单项,将出
9、现如图5.3所示的对话框,选中要应用的Spring的版本及所需的类库文件。图5.3选择Spring版本及类库对话框5.2.2 依赖注入应用选择结束后,单击【Next】按钮,出现如图5.4所示的界面。用于创建Spring的配置文件。单击【Finish】按钮完成。项目的src文件夹下会出现名为applicationContext.xml的文件,这就是Spring的核心配置文件。图5.4创建Spring的配置文件5.2.2 依赖注入应用3.修改测试类修改测试类配置完成后,就可以修改Test类,代码。4.运行运行运行该测试类,结果如图5.5所示。图5.5运行结果5.2.2 依赖注入应用对象ctx就相当
10、于原来的Factory工厂,原来的Factory可以删除掉了。再回头看原来的applicationContext.xml文件配置:id是ctx.getBean的参数值,一个字符串。class是一个类(包名+类名)。然后在Test类里获得Chinese对象及American对象:human=(Human)ctx.getBean(american);human=(Human)ctx.getBean(american);5.2.3 注入的两种方式1.设置注入设置注入设置注入是通过setter方法注入被调用者的实例。这种方法简单、直观,很容易理解,因而Spring的依赖注入被大量使用,下面举例说明。创
11、建一个Java Project,命名为“FactoryExample1”。在项目的src文件夹下建立下面的源文件。Human的接口,Human.java,代码如下:public interface Human void speak();Language接口,Language.java,代码如下:public interface Language public String kind();5.2.3 注入的两种方式下面是Human实现类Chinese.java代码:public class Chinese implements Human private Language lan;public
12、void speak()System.out.println(lan.kind();public void setLan(Language lan)this.lan=lan;下面是Language实现类English.java代码:public class English implements Language public String kind()return 中国人也会说英语!;5.2.3 注入的两种方式下面通过Spring的配置文件来完成其对象的注入。代码如下:5.2.3 注入的两种方式测试代码如下:importorg.springframework.context.Applicati
13、onContext;importorg.springframework.context.support.FileSystemXmlApplicationContext;publicclassTestpublicstaticvoidmain(Stringargs)ApplicationContextctx=newFileSystemXmlApplicationContext(src/applicationContext.xml);Humanhuman=null;human=(Human)ctx.getBean(chinese);human.speak();程序执行结果如图5.6所示。图5.6程序
14、运行结果5.2.3 注入的两种方式2.构造注入构造注入例如,只要对前面的Chinese类进行简单的修改:publicclassChineseimplementsHumanprivateLanguagelan;publicChinese();/构造注入所需要的带参数的构造函数publicChinese(Languagelan)this.lan=lan;publicvoidspeak()System.out.println(lan.kind();5.2.3 注入的两种方式配置文件也需要做简单的修改:5.3 Spring核心接口及基本配置5.3.1 Spring核心接口核心接口1BeanFactor
15、y在Spring中有几种BeanFactory的实现,其中最常使用的是org.springframework.bean.factory.xml.XmlBeanFactory。它根据XML文件中的定义装载Bean。要创建XmlBeanFactory,需要传递一个java.io.InputStream对象给构造函数。InputStream对象提供XML文件给工厂。例如,下面的代码片段使用一个java.io.FileInputStream对象把BeanXML定义文件给XmlBeanFactory:BeanFactoryfactory=newXmlBeanFactory(newFileInputStr
16、eam(applicationContext.xml);这行简单的代码告诉BeanFactory从XML文件中读取Bean的定义信息,但是现在BeanFactory没有实例化Bean,Bean被延迟载入到BeanFactory中,就是说BeanFactory会立即把Bean定义信息载入进来,但是Bean只有在需要的时候才被实例化。为了从BeanFactory得到Bean,只要简单地调用getBean()方法,把需要的Bean的名字当做参数传递进去就行了。由于得到的是Object类型,所以要进行强制类型转化。MyBeanmyBean=(MyBean)factory.getBean(“myBean
17、”);5.3.1 Spring核心接口2.ApplicationContext两者都是载入Bean定义信息,装配Bean,根据需要分发Bean。但是ApplicationContext提供了更多功能:应用上下文提供了文本信息解析工具,包括对国际化的支持。应用上下文提供了载入文本资源的通用方法,如载入图片。应用上下文可以向注册为监听器的Bean发送事件。由于它提供的附加功能,几乎所有的应用系统都选择ApplicationContext,而不是BeanFactory。在ApplicationContext的诸多实现中,有三个常用的实现:lClassPathXmlApplicationContext
18、:从类路径中的XML文件载入上下文定义信息,把上下文定义文件当成类路径资源。lFileSystemXmlApplicationContext:从文件系统中的XML文件载入上下文定义信息。lXmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息。5.3.1 Spring核心接口例如:ApplicationContextcontext=newFileSystemXmlApplicationContext(c:/foo.xml);ApplicationContextcontext=newClassPathApplicationContext(foo.xml);
19、ApplicationContextcontext=WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext();FileSystemXmlApplicationContext和ClassPathXmlApplicationContext的区别是:FileSystemXmlApplicationContext只能在指定的路径中寻找foo.xml文件,而ClassPathXmlApplicationContext可以在整个类路径中寻找foo.xml。5.3.2 Spring
20、基本配置1使用使用XML装配装配理论上,Bean装配可以从任何配置资源获得。但实际上,XML是最常见的Spring应用系统配置源。如下的XML文件展示了一个简单的Spring上下文定义文件:/根元素/Bean实例/Bean实例在XML文件定义Bean,上下文定义文件的根元素。有多个子元素。每个元素定义了一个Bean(任何一个Java对象)如何被装配到Spring容器中。5.3.2 Spring基本配置2添加一个添加一个Bean向Spring容器中添加一个Bean只需要向XML文件中添加一个元素。如下面的语句:当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以
21、为Bean指定特定的作用域。原型模式与单实例模式:Spring中的Bean默认情况下是单实例模式。在容器分配Bean的时候,它总是返回同一个实例。的singleton属性告诉ApplicationContext这个Bean是不是单实例Bean,默认是true,但是把它设置为false的话,就把这个Bean定义成了原型Bean。/原型模式Bean request或session:对于每次HTTP请求或HttpSession,使用request或session定义的Bean都将产生一个新实例,即每次HTTP请求或HttpSession将会产生不同的Bean实例。global session:每个全
22、局的HttpSession对应一个Bean实例。典型情况下,仅在使用portlet context的时候有效。当一个Bean实例化的时候,有时需要做一些初始化的工作,然后才能使用。因此,Spring可以在创建和拆卸Bean的时候调用Bean的两个生命周期方法。5.3.2 Spring基本配置在Bean的定义中设置自己的init-method,这个方法在Bean被实例化时马上被调用。同样,也可以设置自己的destroy-method,这个方法在Bean从容器中删除之前调用。一个典型的例子是连接池Bean。publicclassMyConnectionPoolpublicvoidinitalize
23、()/initializeconnectionpoolpublicvoidclose()/releaseconnectionBean的定义如下:/当Bean从容器中删除时调用close方法5.4 Spring AOP5.4.1 从代理机制初探从代理机制初探AOP来看一个简单的例子,当需要在执行某些方法时留下日志信息,可能会这样写:importjava.util.logging.*;publicclassHelloSpeakerpirvateLoggerlogger=Logger.getLogger(this.getClass().getName();publicvoidhello(String
24、name)logger.log(Level.INFO,hellomethodstarts);/方法开始执行时留下日志Sytem.out.println(hello,+name);/程序的主要功能 Logger.log(Level.INFO,hellomethodends);/方法执行完毕时留下日志在HelloSpeaker类中,当执行hello()方法时,程序员希望该方法执行开始与执行完毕时都留下日志。最简单的做法是用上面的程序设计,在方法执行的前后加上日志动作。5.4.1 从代理机制初探AOP可以使用代理(Proxy)机制来解决这个问题,有两种代理方式:静态代理(staticproxy)和动
25、态代理(dynamicproxy)。在静态代理的实现中,代理类与被代理的类必须实现同一个接口。在代理类中可以实现记录等相关服务,并在需要的时候再呼叫被代理类。这样被代理类就可以仅仅保留业务相关的职责了。举个简单的例子,首先定义一个IHello接口,IHello.java代码如下:publicinterfaceIHellopublicvoidhello(Stringname);然后让实现业务逻辑的HelloSpeaker类实现IHello接口,HelloSpeaker.java代码如下:publicclassHelloSpeakerimplementsIHellopublicvoidhello(
26、Stringname)System.out.println(hello,+name);5.4.1 从代理机制初探AOP可以看到,在HelloSpeaker类中没有任何日志的代码插入其中,日志服务的实现将被放到代理类中,代理类同样要实现IHello接口。HelloProxy.java代码如下:publicclassHelloProxyimplementsIHelloprivateLoggerlogger=Logger.getLogger(this.getClass().getName();privateIHellohelloObject;publicHelloProxy(IHellohelloO
27、bject)this.helloObject=helloObject;publicvoidhello(Stringname)log(hellomethodstarts);/日志服务helloObject.hello(name);/执行业务逻辑log(hellomethodends);/日志服务privatevoidlog(Stringms)logger.log(Level.INFO,msg);5.4.1 从代理机制初探AOP在HelloProxy类的hello()方法中,真正实现业务逻辑前后安排记录服务,可以实际撰写一个测试程序来看看如何使用代理类。publicclassProxyDemopu
28、blicstaticvoidmain(Stringargs)IHelloproxy=newHelloProxy(newHelloSpeaker();proxy.hello(Justin);程序运行结果:hello,Justin5.4.2 动态代理要实现动态代理,同样需要定义所要代理的接口。IHello.java代码如下:publicinterfaceIHellopublicvoidhello(Stringname);然后让实现业务逻辑的HelloSpeaker类实现IHello接口。HelloSpeaker.java代码如下:publicclassHelloSpeakerimplementsI
29、Hellopublicvoidhello(Stringname)System.out.println(Hello,+name);5.4.2 动态代理与上例不同的是,这里要实现不同的代理类:import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class LogHandler implements InvocationHandlerprivate Object sub;public LogHandler()public LogHandler(Object obj)sub=obj;publ
30、ic Object invoke(Object proxy,Method method,Object args)throws Throwable System.out.println(before you do thing);method.invoke(sub,args);System.out.println(after you do thing);return null;5.4.2 动态代理写一个测试程序,使用LogHandler来绑定被代理类。ProxyDemo.java代码如下:import java.lang.reflect.Proxy;public class ProxyDemo p
31、ublic static void main(String args)HelloSpeaker helloSpeaker=new HelloSpeaker();LogHandler logHandler=new LogHandler(helloSpeaker);Class cls=helloSpeaker.getClass();IHello iHello=(IHello)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),logHandler);iHello.hello(Justin);程序运行结果:before yo
32、u do thingHello,Justinafter you do thing5.4.3 AOP术语与概念1cross-cutting concerns在DynamicProxyDemo例子中,记录的动作原先被横切(Cross-cutting)到HelloSpeaker本身所负责的业务流程中。类似于日志这类的动作,如安全检查、事务等服务,在一个应用程序中常被安排到各个类的处理流程之中。这些动作在AOP的术语中称为cross-cuttingconcerns。如图5.7所示,原来的业务流程是很单纯的。图5.7原来的业务流程5.4.3 AOP术语与概念为了加入日志与安全检查等服务,类的程序代码中被
33、硬生生地写入了相关的Logging、Security程序片段,如图5.8所示。图5.8 加入各种服务的业务流程5.4.3 AOP术语与概念2Aspect将散落在各个业务类中的cross-cuttingconcerns收集起来,设计各个独立可重用的类,这种类称为Aspect。例如,在动态代理中将日志的动作设计为LogHandler类,LogHandler类在AOP术语中就是Aspect的一个具体实例。在需要该服务的时候,缝合到应用程序中;不需要服务的时候,也可以马上从应用程序中脱离。应用程序中的可重用组件不用做任何的修改。例如,在动态代理中的HelloSpeaker所代表的角色就是应用程序中可重
34、用的组件,在它需要日志服务时并不用修改本身的程序代码。5.4.4 通知AdvicelSpring提供了5种通知(Advice)类型:Interception Around、Before、After Returning、Throw 和Introduction。它们分别在以下情况被调用:lInterception Around Advice:在目标对象的方法执行前后被调用。lBefore Advice:在目标对象的方法执行前被调用。lAfter Returning Advice:在目标对象的方法执行后被调用。lThrow Advice:在目标对象的方法抛出异常时被调用。lIntroduction
35、Advice:一种特殊类型的拦截通知,只有在目标对象的方法调用完毕后执行。创建一个Before Advice的Web项目,步骤如下。创建一个Web项目,命名为“Spring_Advices”。编写Java类。Before Advice会在目标对象的方法执行之前被呼叫。这个接口提供了获取目标方法、参数及目标对象。MethodBeforeAdvice接口的代码如下:import java.lang.ref.*;import java.lang.reflect.Method;public interface MethodBeforeAdvicevoid before(Method method,Ob
36、ject args,Object target)throws Exception;5.4.4 通知Advice用实例来示范如何使用Before Advice。首先要定义目标对象必须实现的接口IHello。IHello.java代码如下:public interface IHello public void hello(String name);接着定义一个HelloSpeaker,实现IHello接口。HelloSpeaker.java代码如下:public class HelloSpeaker implements IHellopublic void hello(String name)Sy
37、stem.out.println(Hello,+name);5.4.4 通知Advice在对HelloSpeader不进行任何修改的情况下,想要在hello()方法执行之前可以记录一些信息。有一个组件,但没有源代码,可对它增加一些日志的服务。LogBeforeAdvice.java代码如下:import java.lang.reflect.*;import java.util.logging.Level;import java.util.logging.Logger;import org.springframework.aop.MethodBeforeAdvice;public class L
38、ogBeforeAdvice implements MethodBeforeAdviceprivate Logger logger=Logger.getLogger(this.getClass().getName();public void before(Method method,Object args,Object target)throws Exception logger.log(Level.INFO,method starts+method);5.4.4 通知Advice添加Spring开发能力。applicationContext.xml的代码如下:IHellologBeforeA
39、dvice5.4.4 通知Advice运行程序,测试结果。写一个程序测试一下BeforeAdvice的运作。SpringAOPDemo.java代码如下:importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.FileSystemXmlApplicationContext;publicclassSpringAOPDemopublicstaticvoidmain(Stringargs)ApplicationContextcontext=newFileSystemX
40、mlApplicationContext(/WebRoot/WEB-INF/classes/applicationContext.xml);IHellohelloProxy=(IHello)context.getBean(helloProxy);helloProxy.hello(Justin);程序运行结果:Hello,JustinHelloSpeaker与LogBeforeAdvice是两个独立的类。对于HelloSpeaker来说,它不用知道LogBeforeAdvice的存在;而LogBeforeAdvice也可以运行到其他类之上。HelloSpeaker与LogBeforeAdvice
41、都可以重复使用。5.4.5 切入点Pointcut创建一个切入点Pointcut项目,步骤如下。创建一个Web项目,命名为“Spring_Pointcut”。编写Java类。IHello.java代码如下:public interface IHello public void helloNewbie(String name);public void helloMaster(String name);HelloSpeaker类实现IHello接口。HelloSpeaker.java代码如下:public class HelloSpeaker implements IHello public vo
42、id helloNewbie(String name)System.out.println(Hello,+name+newbie!);public void helloMaster(String name)System.out.println(Hello,+name+master!);添加Spring开发能力。applicationContext.xml的代码。5.4.5 切入点Pointcut运行程序,测试结果。SpringAOPDemo.java代码如下:importorg.springframework.context.ApplicationContext;importorg.sprin
43、gframework.context.support.FileSystemXmlApplicationContext;publicclassSpringAOPDemopublicstaticvoidmain(Stringargs)ApplicationContextcontext=newFileSystemXmlApplicationContext(/WebRoot/WEB-INF/classes/applicationContext.xml);IHellohelloProxy=(IHello)context.getBean(helloProxy);helloProxy.helloNewbie
44、(Justin);helloProxy.helloMaster(Tom);程序运行结果:Hello,Justinnewbie!Hello,Tommaster!2009-5-2117:16:34LogBeforeAdvicebefore5.5 Spring事务支持声明式事务管理的配置方式,通常有以下4种:使用TransactionProxyFactoryBean为目标Bean生成事务代理的配置。此方式是最传统、配置文件最臃肿、最难以阅读的方式。采用Bean继承的事务代理配置方式,比较简洁,但依然是增量式配置。采用BeanNameAutoProxyCreator,根据Bean Name自动生成事务
45、代理的方式。这是直接利用Spring的AOP框架配置事务代理的方式,需要对Spring的AOP框架有所理解。但这种方式避免了增量式配置,效果非常不错。采用DefaultAdvisorAutoProxyCreator,直接利用Spring的AOP框架配置事务代理的方式,效果非常不错,只是这种配置方式的可读性不如第3种方式。5.5.1 使用TransactionProxyFactoryBean生成事务代理采用这种方式的配置,配置文件增加得非常快。每个Bean需要两个Bean配置,一个是目标Bean,另外一个是使用TransactionProxyFactoryBean配置一个代理Bean。这是一种最
46、原始的配置方式,其配置文件。5.5.2 利用继承简化配置对于这种情况,Spring提供了Bean与Bean之间的继承,可以简化配置。将大部分的通用配置,配置成事务模板。而实际的事务代理Bean,则继承事务模板。这种配置方式可以减少部分配置代码,其配置文件。5.5.3 用BeanNameAutoProxyCreator自动创建事务代理下面介绍一种优秀的事务代理配置策略:采用这种配置策略,完全可以避免增量式配置,所有的事务代理由系统自动创建。容器中的目标Bean自动消失,避免需要使用嵌套Bean来保证目标Bean不可被访问。下面是采用BeanNameAutoProxyCreator配置事务代理的配
47、置文件。TranscationInterceptor是一个事务拦截器Bean,需要传入一个TransactionManager的引用。配置中使用Spring依赖注入该属性,事务拦截器的事务属性通过transactionAttributes来指定,该属性有props子元素,配置文件中定义了2个事务传播规则。5.5.4 用DefaultAdvisorAutoProxyCreator自动创建事务代理采用DefaultAdvisorAutoProxyCreator的事务代理配置方式更加简单,这个代理生成器自动搜索Spring容器中的Advisor,并为容器中所有的Bean创建代理。相对前一种方式,这种
48、方式的可读性不如前一种直观,所以还是推荐采用前一种配置方式。下面是该方式下的配置文件。不管是哪种方式,都定义了事务传播的属性,下面具体介绍事务传播的种类。Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播。lPROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务;lPROPAGATION_SUPPORTS:支持当前事务。如果当前没有事务,就以非事务方式执行。lPROPAGATION_MANDATORY:使用当前的事务。如果当前没有事务,就抛出异常。lPROPAGATION_R
49、EQUIRES_NEW:新建事务。如果当前存在事务,把当前事务挂起。lPROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。lPROPAGATION_NEVER:以非事务方式执行。lPROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。5.6 Spring与Struts 2整合应用开发一个Spring与Struts 2的整合项目的步骤如下。创建Web项目Struts_Spring。添加Struts 2框架。添加5个Jar包:struts 2-core-2.0.14.jar,xwork-2.0.4.jar,ognl-2
50、.6.11.ar,common-logging-1.0.4.jar,freemarker-2.3.8.jar。配置web.xml,代码如下:struts2 org.apache.struts2.dispatcher.FilterDispatcher struts2 /*5.6 Spring与Struts 2整合应用创建login.jsp。login.jsp代码如下:登录界面5.6 Spring与Struts 2整合应用 创建Action。LoginAction.java代码如下:package org.action;import com.opensymphony.xwork2.ActionSu