您的当前位置:首页正文

Spring

2021-10-28 来源:步旅网
L o g oSpring简介www.csst.com.cn

简介

与Hibernate、Struts一样,Spring也是一个开源项目,它的作者是Rod Johnson,官方网站是http://www.springframework.org/。

Spring的基础思想来源于Rod Johnson的一本著名的j2ee书籍:Expert One-on-One J2EE Design and Development 。在这本书中,Rod Johnson列举EJB的种种问题,并提出了相应的解决办法。

从那时起,人们对于EJB的狂热追捧才算结束,转而进入更理性的时代。

简介

Rod Johnson是悉尼大学博士,猜猜他的专业是什么?

Rod Johnson在开发出Spring之前,主要从事项目开发咨询与培训工作。在Spring被广泛认可之后,创办了interface21公司,致力于Spring咨询与培训.

Rod Johnson还是JDO2.0和Servlet2.4专家组成员。

简介

Spring核心技术包括两个方面,一是控制反转

(Inversion of Control, IoC),另一个是面向方面编程(Aspect Oriented Programming, AOP)。Spring囊括了十分丰富的内容,包括表述层、数据层,它提供了许多原来只有EJB才能提供的功能(如宣称式的事务管理),但Spring又无须运行在EJB容器上。无论Spring涉足到哪一个领域,使用的都是简单的JavaBean,一般无须再实现复杂的接口。

Spring框架结构

Spring框架结构

Core封装包是框架的最基础部分,提供IoC和依赖注入特性。这里的基础概念是BeanFactory,它提供对

Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。Dao提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码。并且,JDBC封装包还提供了一种比编程性更好的声明性事务管理方法,不仅仅是实现了特定接口,而且对所有的POJOs(plain old Java ob jects)都适用。

ORM封装包提供了常用的“对象/关系”映射APIs的集成层。其中包括JPA、JDO、Hibernate和iBatis。利用ORM封装包,可以混合使用所有Spring提供的特性进行“对象/关系”映射,如前边提到的简单声明性事务管

Spring框架结构

Spring的AOP封装包提供了符合AOP Alliance规范的面向方面的编程(aspect-oriented programming)实现,让你可以定义,例如方法拦截器(method-interceptors)和切点(pointcuts),从逻辑上讲,从而减弱代码的功能耦合,清晰的被分离开。而且,利用source-level的元数据功能,还可以将各种行为信息合并到你的代码中,这有点象.Net的attribute的概念。

Spring框架结构

Spring中的Web包提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IoC容器初始化和针对Web的application context。当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。

Spring中的MVC封装包提供了Web应用的Model-View-Controller(MVC)实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和web form之间。并且,还可以借助Spring框架的其他特性。

准备工作

下载SpringFramework的最新版本,并解压缩到指定目录。

在IDE中新建一个项目,并将Spring.jar将其相关类库加入项目。

Spring采用Apache common_logging,并结合Apache log4j作为日志输出组件。为了在调试过程中能观察到Spring的日志输出,在CLASSPATH中新建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=%c{1} -%m%n

概述

Spring Framework中目前最引人注目的,也就是名为控制反转(IOC =Inverse Of Control)或者依赖注入(DI =Dependence Injection)的设计思想,这的确是相当优秀的设计理念,但是,光一个单纯的设计模式并不能使得Spring如此成功,而Spring最成功的地方也并不仅仅在于采用了IOC/DI的设计,Spring涵盖了应用系统开发所涉及的大多数技术范畴,包括MVC、ORM以及Remote Interface等,这些技术往往贯穿了大多数应用系统的开发过程。Spring从开发者的角度对这些技术内容进行了进一步的封装和抽象,使得应用开发更为简便。

概述

Spring并非一个强制性框架,它提供了很多独立的组件可供选择。在一些项目中,就仅引用了Spring的ORM模板机制对数据存取层进行处理,并取得了相当理想的效果。评定一个框架是否优良的条件固然有很多种,但是笔者始终认为,对于应用系统开发而言,我们面临着来自诸多方面的压力,此时,最能提高生产力的技术,也就是最有价值的技术。很高兴,Spring让笔者找到了这样的感觉。

Spring 基础语义

何谓控制反转(IoC= Inversion of Control),何谓依赖注入(DI = Dependency Injection)?

IoC,用白话来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。相对IoC而言,“依赖注入”的确更加准确的描述了这种古老而又时兴的设计理念。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。

Spring 基础语义依赖注入实例:

为什么称之为“古老而又时兴”的设计理念?至于“时兴”自然不必多费唇舌,看看国内外大小论坛上当红的讨论主题便知。至于“古老”……,相信大家对下面图片中的设备不会陌生:

Spring 基础语义

这就是大家主要工作装备,IBM T40笔记本电脑一台、USB硬盘和U盘各一只。想必大家在日常工作中也有类似的一套行头。这与依赖注入有什么关系?

图中三个设备都有一个共同点,都支持USB 接口。当我们需要将数据复制到外围存储设备时,可以根据情况,选择是保存在U盘还是USB硬盘,下面的操作大家也都轻车熟路,无非接通USB接口,然后在资源浏览器中将选定的文件拖放到指定的盘符。这样的操作在过去几年中每天都在我们身边发生,而这也正是所谓依赖注入的一个典型案例,上面称之为“古老”想必也不为过分。

再看上例中,笔记本电脑与外围存储设备通过预先指定的一个接口(USB)相连,对于笔记本而言,只是将用户指

Spring 基础语义

定的数据发送到USB接口,而这些数据何去何从,则由当前接入的USB设备决定。在USB设备加载之前,笔记本不可能预料用户将在USB接口上接入何种设备,只有USB设备接入之后,这种设备之间的依赖关系才开始形成。

对应上面关于依赖注入机制的描述,在运行时(系统开机,USB 设备加载)由容器(运行在笔记本中的Windows操作系统)将依赖关系(笔记本依赖USB设备进行数据存取)注入到组件中(Windows文件访问组件)。这就是依赖注入模式在现实世界中的一个版本。

依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。

Spring 基础语义

依赖注入的几种实现类型:

Type1接口注入:

public class ClassA {

private InterfaceB clzB;public doSomething() {

Ojbect obj

=Class.forName(Config.BImplementation).newInstance();

clzB = (InterfaceB)obj;clzB.doIt()}……}

Spring 基础语义

Type2设值注入

在各种类型的依赖注入模式中,设值注入模式在实际开发中得到了最广泛的应用(其中很大一部分得力于Spring框架的影响)。在笔者看来,基于设置模式的依赖注入机制更加直观、也更加自然。Quick Start中的示例,就是典型的设置注入,即通过类的setter方法完成依赖关系的设置。

Spring 基础语义

Type3构造子注入

构造子注入,即通过构造函数完成依赖关系的设定,如:

public class DIByConstructor {

private final DataSource dataSource;private final String message;

public DIByConstructor(DataSource ds, String msg) {

this.dataSource = ds;this.message = msg;}……}

Spring 基础语义

几种依赖注入模式的对比总结:

接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他两种注入模式,因而在IOC的专题世界内并不被看好。Type2和Type3型的依赖注入实现则是目前主流的IOC实现模式。这两种实现方式各有特点,也各具优势.

Spring 基础语义

Type2 设值注入的优势

1.对于习惯了传统JavaBean开发的程序员而言,通过setter方法设定依赖关系显得更加直观,更加自然。

2.如果依赖关系(或继承关系)较为复杂,那么Type3模式的构造函数也会相当庞大(我们需要在构造函数中设定所有依赖关系),此时Type2模式往往更为简洁。3.对于某些第三方类库而言,可能要求我们的组件必须提供一个默认的构造函数(如Struts中的Action),此时Type3类型的依赖注入机制就体现出其局限性,难以完成我们期望的功能。

Spring 基础语义

Type3 构造子注入的优势:

1.“在构造期即创建一个完整、合法的对象”,对于这条Java设计原则,Type3无疑是最好的响应者。

2.避免了繁琐的setter方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,更加易读。

3.由于没有setter方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后即处于相对“不变”的稳定状态,无需担心上层代码在调用过程中执行setter方法对组件依赖关系产生破坏,特别是对于Singleton模式的组件而言,这可能对整个系统产生重大的影响。

4.同样,由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖关系。对调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的层次清晰性提供了保证。

Spring 基础语义

5.通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要,比如某个依赖关系注入的先决条件是组件的DataSource及相关资源已经被设定。

可见,Type3和Type2模式各有千秋,而Spring、

PicoContainer都对Type3和Type2类型的依赖注入机制提供了良好支持。这也就为我们提供了更多的选择余地。理论上,以Type3类型为主,辅之以Type2类型机制作为补充,可以达到最好的依赖注入效果,不过对于基于Spring Framework开发的应用而言,Type2使用更加广泛。

Spring Bean封装机制

概述:

Spring 从核心而言,是一个DI 容器,其设计哲学是提供一种无侵入式的高扩展性框架。即无需代码中涉及Spring专有类,即可将其纳入Spring容器进行管理。

作为对比,EJB则是一种高度侵入性的框架规范,它制定了众多的接口和编码规范,要求实现者必须遵从。侵入性的后果就是,一旦系统基于侵入性框架设计开发,那么之后任何脱离这个框架的企图都将付出极大的代价。

为了避免这种情况,实现无侵入性的目标。Spring 大量引入了Java 的Reflection机制,通过动态调用的方式避免硬编码方式的约束,并在此基础上建立了其核心组件BeanFactory,以此作为其依赖注入机制的实现基础。

Spring Bean封装机制

概述:

org.springframework.beans包中包括了这些核心组件的实现类,核心中的核心为BeanWrapper和

BeanFactory类。这两个类从技术角度而言并不复杂,但对于Spring 框架而言,却是关键所在,如果有时间,建议对其源码进行研读,必有所获。

Spring Bean封装机制Bean Wrapper

BeanWrapper提供了设置和获取属性值(单个的或者是批量的),获取属性描述信息、查询只读或者可写属性等功能。不仅如此,

BeanWrapper还支持嵌套属性,你可以不受嵌套深度限制对子属性的值进行设置。看看如何通过Spring BeanWrapper操作一个JavaBean:

Object obj = Class.forName(\"net.xiaxin.beans.User\").

newInstance();

BeanWrapper bw = new BeanWrapperImpl(obj);bw.setPropertyValue(\"name\System.out.println(\"Username=>\"+

bw.getPropertyValue(\"name\"));

通过这样的方式设定Java Bean属性实在繁琐,但它却提供了一个通用的属性设定机制,而这样的机制,也正是Spring依赖注入机制所依

Spring Bean封装机制Bean Wrapper

通过BeanWrapper,我们可以无需在编码时就指定

JavaBean的实现类和属性值,通过在配置文件加以设定,就可以在运行期动态创建对象并设定其属性(依赖关系)。上面的代码中,我们仅仅指定了需要设置的属性名

“name”,运行期,BeanWrapper将根据JavaBean规范,动态调用对象的“setName”方法进行属性设定。属性名可包含层次,如对于属性名“address.zipcode”,BeanWrapper会调用“getAddress().setZipcode”方法。

Spring Bean封装机制Bean Factory

Bean Factory顾名思义,负责创建并维护Bean实例。Bean Factory负责根据配置文件创建Bean实例,可以配置的项目有:

1.Bean属性值及依赖关系(对其他Bean的引用)2.Bean创建模式(是否Singleton模式,即是否只针对指定类维持全局唯一的实例)3.Bean初始化和销毁方法4.Bean的依赖关系

Spring Bean封装机制Bean Factory

下面是一个较为完整的Bean配置示例:

Spring Bean Configuration Sampleid=\"TheAction\" ⑴

class=\"net.xiaxin.spring.qs.UpperAction\" ⑵singleton=\"true\" ⑶init-method=\"init\" ⑷

destroy-method=\"cleanup\" ⑸

depends-on=\"ActionManager\" ⑹>

Spring Bean封装机制



HeLLo

class=\"org.springframework.jndi.JndiObjectFactoryBean\">

java:comp/env/jdbc/sample

Spring Bean封装机制Bean Factory

⑴id

Java Bean在BeanFactory中的唯一标识,代码中通过BeanFactory获取JavaBean实例时需以此作为索引名称。⑵class

Java Bean 类名⑶singleton

指定此Java Bean是否采用单例(Singleton)模式,如果设为“true”,则在BeanFactory作用范围内,只维护此Java Bean的一个实例,代码通过BeanFactory获得此Java Bean实例的引用。反之,如果设为“false”,则通过

BeanFactory获取此Java Bean实例时,BeanFactory每次都将创建一个新的实例返回。

Spring Bean封装机制

⑷init-method

初始化方法,此方法将在BeanFactory创建JavaBean实例之后,在向应用层返回引用之前执行。一般用于一些资源的初始化工作。

⑸destroy-method

销毁方法。此方法将在BeanFactory销毁的时候执行,一般用于资源释放。⑹depends-on

Bean依赖关系。一般情况下无需设定。Spring会根据情况组织各个依赖关系的构建工作(这里示例中的depends-on属性非必须)。只有某些特殊情况下,如JavaBean中的某些静态变量需要进行初始化(这是一种BadSmell,应该在设计上应该避免)。通过depends-on指定其依赖关系可保证在此

Bean加载之前,首先对depends-on所指定的资源进行加载。

Spring Bean封装机制

⑺

通过节点可指定属性值。BeanFactory将自动根据Java Bean对应的属性类型加以匹配。

下面的”desc”属性提供了一个null值的设定示例。注意代表一个空字符串,如果需要将属性值设定为null,必须使用节点。⑻

指定了属性对BeanFactory中其他Bean的引用关系。示例中,TheAction的dataSource属性引用了id为

cataSource的Bean。BeanFactory将在运行期创建

dataSourcebean实例,并将其引用传入TheActionBean的dataSource属性。

Spring Bean封装机制

下面的代码演示了如何通过BeanFactory获取Bean实例:

InputStreamis = new FileInputStream(\"bean.xml\");XmlBeanFactoryfactory = new XmlBeanFactory(is);Action action= (Action) factory.getBean(\"TheAction\");

此时我们获得的Action实例,由BeanFactory进行加载,并根据配置文件进行了初始化和属性设定。

联合上面关于BeanWrapper的内容,我们可以看到,BeanWrapper实现了针对单个Bean的属性设定操作。而BeanFactory则是针对多个Bean的管理容器,根据给定的配置文件,BeanFactory从中读取类名、属性名/值,然后通过Reflection机制进行Bean加载和属性设定。

Spring Bean封装机制ApplicationContext

BeanFactory提供了针对Java Bean的管理功能,而ApplicationContext提供了一个更为框架化的实现(从上面的示例中可以看出,BeanFactory的使用方式更加类似一个API,而非Framework style)。

ApplicationContext覆盖了BeanFactory的所有功能,并提供了更多的特性。此外,ApplicationContext为与现有应用框架相整合,提供了更为开放式的实现(如对于Web应用,我们可以在web.xml中对

ApplicationContext进行配置)。相对BeanFactory而言,ApplicationContext提供了以下扩展功能:

Spring Bean封装机制ApplicationContext

1.国际化支持

我们可以在Beans.xml文件中,对程序中的语言信息(如提示信息)进行定义,将程序中的提示信息抽取到配置文件中加以定义,为我们进行应用的各语言版本转换提供了极大的灵活性。

2.资源访问支持对文件和URL的访问。3.事件传播

事件传播特性为系统中状态改变时的检测提供了良好支持4.多实例加载

可以在同一个应用中加载多个Context实例。

Spring Bean封装机制ApplicationContext

1) 国际化支持

国际化支持在实际开发中可能是最常用的特性。对于一个需要支持不同语言环境的应用而言,我们所采取的最常用的策略一般是通过一个独立的资源文件(如一个

properties文件)完成所有语言信息(如界面上的提示信息)的配置,Spring对这种传统的方式进行了封装,并提供了更加强大的功能,如信息的自动装配以及热部署功能(配置文件修改后自动读取,而无需重新启动应用程序),下面是一个典型的示例:

Spring Bean封装机制

Spring Quick Startmessages

Spring Bean封装机制

这里声明了一个名为messageSource的Bean(注意对于Message定义,Bean ID必须为messageSource,这是目前Spring的编码规约),对应类为

ResourceBundleMessageSource,目前Spring中提供了两个MessageSource接口的实现,即ResourceBundleMessageSource和

ReloadableResourceBundleMessageSource,后者提供了无需重启即可重新加载配置信息的特性。在配置节点中,我们指定了一个配置名“messages”。

Spring会自动在CLASSPATH根路径中按照如下顺序搜寻配置文件并进行加载(以Locale为zh_CN为例):

Spring Bean封装机制



messages_zh_CN.propertiesmessages_zh.propertiesmessages.propertiesmessages_zh_CN.classmessages_zh.classmessages.class

示例中包含了两个配置文件,内容如下:messages_zh_CN.properties:

userinfo=当前登录用户: [{0}] 登录时间:[{1}]messages_en_US.properties:

userinfo=Current Login user: [{0}] Login time:[{1}]

Spring Bean封装机制

我们可以通过下面的语句进行测试:

ApplicationContext ctx=new

FileSystemXmlApplicationContext(\"bean.xml\");Object[] arg = new Object[]{\"Erica\

Calendar.getInstance().getTime()

};

//以系统默认Locale加载信息(对于中文WinXP而言,默认为zh_CN)

String msg = ctx.getMessage(\"userinfo\arg);

System.out.println(\"Message is ===> \"+msg);

Spring Bean封装机制2) 资源访问

ApplicationContext.getResource方法提供了对资源文件访问支持,如:Resource rs=

ctx.getResource(\"classpath:config.properties\");File file= rs.getFile();

上例从CLASSPATH根路径中查找config.properties文件并获取其文件句柄。

getResource方法的参数为一个资源访问地址,如:file:C:/config.properties/config.properties

classpath:config.properties

注意getResource返回的Resource并不一定实际存在,可以通过Resource.exists()方法对其进行判断。

Spring Bean封装机制

3) 事件传播

ApplicationContext基于Observer模式(java.util包中有对应实现),提供了针对Bean的事件传播功能。通过Application. publishEvent方法,我们可以将事件通知系统内所有的ApplicationListener。

事件传播的一个典型应用是,当Bean中的操作发生异常(如数据库连接失败),则通过事件传播机制通知异常监听器进行处理。

Spring Bean封装机制Web Context

上面的示例中,ApplicationContext均通过编码加载。对于Web应用,Spring提供了可配置的ApplicationContext加载机制。加载器目前有两种选择:ContextLoaderListener和

ContextLoaderServlet。这两者在功能上完全等同,只是一个是基于Servlet2.3版本中新引入的Listener接口实现,而另一个基于Servlet接口实现。开发中可根据目标Web容器的实际情况进行选择。配置非常简单,在web.xml中增加:

org.springframework.web.context.ContextLoaderListener

Spring Bean封装机制

或:

context

org.springframework.web.context.ContextLoaderServlet

1

通过以上配置,Web容器会自动加载/WEB-INF/applicationContext.xml初始化

ApplicationContext实例,如果需要指定配置文件位置,可通过context-param加以指定:

Spring Bean封装机制

contextConfigLocation/WEB-INF/myApplicationContext.xml

配置完成之后,即可通过

WebApplicationContextUtils.getWebApplicationContext方法在Web应用中获取ApplicationContext引用。

AOP

AOP是OOP的延续,是Aspect Oriented

Programming的缩写,意思是面向方面编程。面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。除了类(classes)以外,AOP提供了切面。切面对关注点进行模块化,例如横切多个类型和对象的事务管理。(这些关注点术语通常称作横切(crosscutting)关注点。)Spring的一个关键的组件就是AOP框架。尽管如此,Spring IoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得Spring IoC容器更加完善。

AOP

Spring中所使用的AOP:

提供声明式企业服务,特别是为了替代EJB声明式服务。最重要的服务是声明性事务管理(declarative transaction management),这个服务建立在Spring的抽象事务管理(transaction abstraction)之上。允许用户实现自定义的切面,用AOP来完善OOP的使用。这样你可以把Spring AOP看作是对Spring的一种增强,它使得Spring可以不需要EJB就能提供声明式事务管理;或者也可以使用Spring AOP框架的全部功能来实现自定义的切面。

AOP

举例:假设有在一个应用系统中,有一个共享的数据必须被并发同时访问,首先,将这个数据封装在数据对象中,称为Data Class,同时,将有多个访问类,专门用于在同一时刻访问这同一个数据对象。

为了完成上述并发访问同一资源的功能,需要引入锁Lock的概念,也就是说,某个时刻,当有一个访问类访问这个数据对象时,这个数据对象必须上锁Locked,用完后就立即解锁unLocked,再供其它访问类访问。使用传统的编程习惯,我们会创建一个抽象类,所有的访问类继承这个抽象父类,如下:abstract class Worker{

abstract void locked();

abstract void accessDataObject();abstract void unlocked();

}

AOP

缺点:

accessDataObject()方法需要有“锁”状态之类的相关代码。

Java只提供了单继承,因此具体访问类只能继承这个父类,如果具体访问类还要继承其它父类,比如另外一个如Worker的父类,将无法方便实现。

重用被打折扣,具体访问类因为也包含“锁”状态之类的相关代码,只能被重用在相关有“锁”的场合,重用范围很窄.

AOP

仔细研究这个应用的“锁”,它其实有下列特性:

“锁”功能不是具体访问类的首要或主要功能,访问类主要功能是访问数据对象,例如读取数据或更改动作。

“锁”行为其实是和具体访问类的主要功能可以独立、区分开来的。

“锁”功能其实是这个系统的一个纵向切面,涉及许多类、许多类的方法。

如下图:

AOP

因此,一个新的程序结构应该是关注系统的纵向切面,例如这个应用的“锁”功能,这个新的程序结构就是aspect(方面)

AOP

AOP应用范围

很明显,AOP非常适合开发J2EE容器服务器,目前JBoss 4.0正是使用AOP框架进行开发。具体功能如下:Authentication 权限Caching 缓存Context passing 内容传递Error handling 错误处理Lazy loading懒加载Debugging调试logging, tracing记录跟踪优化校准Performance optimization性能优化Persistence持久化

Resource pooling资源池Synchronization同步Transactions 事务

AOP

AOP具体实现

AOP是一个概念,并没有设定具体语言的实现,它能克服那些只有单继承特性语言的缺点(如Java),目前AOP具体实现有以下几个项目:AspectJ (TM):创建于Xerox PARC. 有近十年历史,成熟

缺点:过于复杂;破坏封装;需要专门的Java编译器

动态AOP:使用JDK的动态代理API或字节码Bytecode处理技术。

基于动态代理API的具体项目有:JBoss 4.0服务器nanning这是以中国南宁命名的一个项目,搞不清楚为什么和中国相关?是中国人发起的?

因篇幅问题不能全部显示,请点此查看更多更全内容