Spring

本文介绍了Spring框架的核心技术,包括IoC容器、DI、面向接口编程、AOP、事务管理等,并详细探讨了Spring如何与Struts2、Hibernate等框架集成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 SpringIoC

IoCInverse of Control,控制反转,是指一个系统中的类的实例化是由类自身来控制的,而不是编程者通过代码来控制的。

通常我们在代码中使用一个类(例如Student类)的时候,需要自己在代码中实例化它,如下:

Student stu = new Student();

这样导致这段代码对这个类的依赖是硬编码依赖,也就是说类的名称的改变必然导致该源代码的修改,如果一个系统中这样的代码非常多,那么这个系统的可维护性和可移植性都会大打折扣。要想改变类的实现或名称后,源代码不改变就必须使用接口来实现多态,于是上述代码可以变成一个接口IStudent和实现了该接口的一个类Student的方式,如下:

IStudent stu = new Student();

用接口改写代码后,代码左边对接口的引用对于不同的实现类来说,就可以不用改变了,但是代码的右侧还是出现了类的名称,当类改变的时候,右边代码还是需要改变的,那么如何能做到使用不同实现类的时候,右侧的代码不动呢?答案就是使用IoC

MyEclispe5.1所支持的Spring版本为2.0,为了使用最新的Spring2.5.5,我们按如下步骤建立工程。

1. 建立web project,名称“SpringTest”,存放路径“f:\SpringTest”;

2. 引入Spring2.5.5jar包,把Spring2.5.5中所带的spring.jar文件和日志文件的jarcommons-logging-1.0.4.jar复制到WEB-INF/lib目录中;

3. 在src目录下建立bean.xml文件用于配置PartStudent类的实例;

<bean id="stu" class="mypack.PartStudent"/>

4. 写一个main函数,用于生成PartStudent类的实例;

package mypack;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestStudent {

public static void main(String[] args) {

//从类路径上读取配置文件bean.xml

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"bean.xml"});

//通过id="stu"找到类PartStudent的实例

IStudent stu  = (IStudent)context.getBean("stu");

//调用方法doLession()

stu.doLession();

}

}

代码IStudent stu = (IStudent)context.getBean(“stu”);就只依赖于接口IStudent和支付串“stu”,以后只要更改配置文件bean.xmlstu所指向的class,不用修改任何源代码方法doLession()也可以输出不同的内容。这就是IoC所发挥的作用。

同时Spring所产生的PartStudent类的实例还是一个单实例,测试如下:

通过Spring框架所产生的类都是单实例的,单实例类正好是业务层的类所希望的,所以我们经常说Spring是一个业务层的框架,它使得业务层的类自动成为单实例。

Spring所产生的类(bean)默认是单实例的,但是也可以产生多实例的类,产生类的作用域范围见表10-1所示。

10-1 bean的作用域

作用域

描述

singleton

每个bean定义对应一个bean的实例

prototype

一个bean定义对应多个对象的实例

request

每次http请求都会有各自的bean实例,该作用域仅在基于webSpring ApplicationContext情形下有效

session

在一个http session中,一个bean定义对应一个bean实例,该作用域仅在基于webSpring ApplicationContext情形下有效

global session

在一个全局的http session中,一个bean定义对应一个bean实例,该作用域仅在基于webSpring ApplicationContext情形下有效

作用域的配置在bean.xml中使用scope属性来表示:

<bean id="stu" class="mypack.PartStudent" scope="prototype"/>

二、 SpringDI

典型的企业应用不会只由单一的对象(或Spring的术语bean)组成。毫无疑问,即使最简单的系统也需要多个对象共同来展示给用户一个整体的应用。那么这些bean对象如何在一起协同工作呢。

DIDependency Injection,依赖注入,对象之间的依赖关系由bean对象自己来处理,这样可以使得类之间达到更高层次的松耦合。DI主要有两种注入方式:Setter注入和构造子注入。

我们在PartStudent类中需要设置一个属性:学生姓名,如何初始化这个姓名属性呢?一种办法是通过set/get方法来实现,另一种办法是通过带参构造来实现。

l Setter注入

为了给name赋上值“张三”,我们在bean.xml中配置<property/>标记:

<bean id="stu" class="mypack.PartStudent">

  <property name="name" value="张三"/>

</bean>

这个配置会使用setName()方法,把“张三”的值赋给属性name

l 构造子注入

为了通过带参构造给name属性赋值,bean.xml的配置情况如下:

<bean id="stu" class="mypack.PartStudent">

  <constructor-arg index="0" value="张三"/>

</bean>

index=”0”表示构造函数中第一个参数的值被设置为“张三”。

依赖注入不仅可以注入基本类型的值,还可以把具有依赖关系的对象的值注入其中,给PartStudent类添加属性birthday,它表示学生的生日,其类型为类Birthday

三、 Spring面向接口的编程

程序开发中有一个著名的原则,叫“开闭原则”,说的是程序对于修改是关闭的,而对于扩展是开放的。当使用Spring框架以及接口编程的时候,这一原则得到了很好的遵守。例如我们希望添加一个周一到周五上课的学生类,它的doLession()方法输出“周一到周五上课”。开闭原则要求我们不能去修改原来的代码来实现,因此我们只能扩展原来的代码,利用Spring框架和接口的多态来实现。见下面代码:

四、 SpringAOP

AOPAspect Oriant Programming,面向切面编程,弥补了面向对象编程的不足。在面向对象的编程中,我们研究的是类之间的纵向联系,继承就是纵向联系的代表,但是要想实现横向联系就很困难,比如要求系统中每个类的某些方法调用之前,都要执行一段公用代码,这个要求在面向对象的编程体系中很难优雅的实现,而实际程序设计中这样的要求很普遍,例如事务处理和权限检查,都是在执行某些方法时需要特别执行的公共代码。这段公共代码或方法就是我们所说的切面(Aspect),它是一组特定的功能集合,需要触发切面功能执行的那些方法的声明就称为切入点(Pointcut),切面功能在这些方法调用之前、调用之后或前后都有或方法抛出异常的时候得到执行,这些执行时刻就称为通知(Advice),需要执行的这些方法可以称为连接点(Joinpoint)。

通知类型按切面功能调用的不同时刻,可以分为:

l 前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。 

l 后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。 

l 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。 

l 最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。 

l 环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。 

环绕通知是最常用的通知类型。和AspectJ一样,Spring提供所有类型的通知,我们推荐你使用尽可能简单的通知类型来实现需要的功能。例如,如果你只是需要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知,尽管环绕通知也能完成同样的事情。用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。比如,你不需要在JoinPoint上调用用于环绕通知的proceed()方法,就不会有调用的问题。 

通过切入点匹配连接点的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。切入点使得通知可以独立对应到面向对象的层次结构中。例如,一个提供声明式事务管理的环绕通知可以被应用到一组横跨多个对象的方法上(例如服务层的所有业务操作)。

五、 Spring集成Struts2框架

Struts2Spring集成时,Action的实例化由SpringIoC来控制。要做到这一点,必须在struts.xml文件配置常量struts.objectFactory

<!—Action实例的产生由spring负责   --> 

<constant name="struts.objectFactory" value="spring"/>

同时在web工程中还要引入Struts2针对Spring发布的plugIn插件包struts2-spring-plugin-2.0.11.jar以及spring.jar

Springbean的配置文件WEB-INF\applicationContext.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<!-- 业务类 -->

<bean id="cs" class="service.CheckService" />

<!-- Action 多实例 -->

<bean id="check" class="mypack.LoginAction" scope="prototype">

  <property name="checkservice" ref="cs"/>

</bean>

</beans>

Action作为一个bean配置在Spring之中,由于Action中可能配置有属性,所以要求Action所对应的bean必须是多实例的,即scope="prototype"。

六、 Spring集成Hibernate

Spring通过HibernateTemplate提供对Hibernate的集成,这样做的主要目的是为了能够清晰地划分应用程序层次而不管使用何种数据访问和事务管理技术,从而降低各个应用程序对象之间的耦合。业务逻辑不再依赖于特定的数据访问与事务策略;不再有硬编码的资源查找、不再有难以替换的singletons、不再有用户自定义的服务注册。

Spring提供了一个简单且稳固的方案使得各种应用逻辑对象连接在一起,使这些对象可重用,并尽可能不依赖容器。所有的数据访问技术都能独立使用,但是他们在Spring提供的基于XML配置且无需依赖Spring的普通JavaBean下会与application Context整合的更好。在典型的Spring应用程序中,很多重要的对象都是JavaBeans:数据访问template、数据访问对象(使用template)、事务管理器、业务逻辑对象(使用数据访问对象和事务管理器)、web视图解析器、web控制器(使用业务服务)等等。

为了避免硬编码的资源查找与应用程序对象紧密耦合,Spring允许你在Spring容器中以bean的方式定义诸如JDBC DataSource或者Hibernate SessionFactory 的数据访问资源。任何需要进行资源访问的应用程序对象只需要持有这些事先定义好的实例的引用就可以了,下面的代码演示如何创建一个JDBC DataSource 和Hibernate SessionFactory。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<!-- 定义一个数据源连接池 -->

<bean id="myDataSource"

class="org.apache.commons.dbcp.BasicDataSource"

destroy-method="close">

<property name="driverClassName"

value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />

<property name="url"

value="jdbc:sqlserver://localhost:1433;databasename=northwind" />

<property name="username" value="sa" />

<property name="password" value="" />

</bean>

<!-- 定义供Hibernate使用的SessionFactory -->

<bean id="mySessionFactory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

<property name="dataSource" ref="myDataSource" />

<property name="mappingResources">

<list>

<value>product.hbm.xml</value>

</list>

</property>

<property name="hibernateProperties">

<value>

hibernate.dialect=org.hibernate.dialect.SQLServerDialect

</value>

</property>

</bean>

</beans>

将一个本地定义的,如Jakarta Commons DBCP的 BasicDataSource 切换为一个JNDI定位的DataSource(通常由应用程序服务器管理),仅仅需要改变配置:

<bean id="myDataSource"

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

<property name="jndiName" value="java:comp/env/jdbc/myds" />

</bean>

定义一个DAO,继承基类HibernateDaoSupport使用HibernateTemplate来实现该DAO,代码如下:

<bean id="myProductDao" class="product.ProductDaoImpl">

<property name="sessionFactory" ref="mySessionFactory" />

</bean>

作为不使用Spring的 HibernateTemplate 来实现DAO的替代解决方案,你依然可以用传统的编程风格来编写你的数据访问代码。 无需将你的Hibernate访问代码包装在一个回调中,只需符合Spring的通用的 DataAccessException 异常体系。 HibernateDaoSupport 基类提供了访问与当前事务绑定的 Session 对象的函数,因而能保证在这种情况下异常的正确转化。 类似的函数同样可以在 SessionFactoryUtils 类中找到,但他们以静态方法的形式出现。 值得注意的是,通常将 'false' 作为参数值(表示是否允许创建)传递给 getSession(..) 方法进行调用。 此时,整个调用将在同一个事务内完成(它的整个生命周期由事务控制,避免了关闭返回的 Session 的需要)。 

这种直接使用Hibernate访问代码的好处在于它允许你在数据访问代码中抛出 任何 checked exception,而 HibernateTemplate 却受限于回调中的unchecked exception。 注意,你通常可以将这些应用程序的异常处理推迟到回调函数之后,这样,你依然可以正常使用 HibernateTemplate。 一般来说,HibernateTemplate 类所提供的许多方法在许多情况下看上去更简单和便捷。

七、 Spring基于AOP的事务处理

Spring框架引人注目的重要因素之一是它全面的事务支持。Spring框架提供了一致的事务管理抽象,这带来了以下好处:

1. 为复杂的事务API提供了一致的编程模型,如JTAJDBCHibernateJPAJDO

2. 支持声明式事务管理

3. 提供比大多数复杂的事务API(诸如JTA)更简单的,更易于使用的编程式事务管理API

4. 非常好地整合Spring的各种数据访问抽象。

实际使用的时候,优先使用声明式事务,它代码简单、容易理解。我们假设有一个服务类DefaultFooService.java及其接口FooService.java,代码如下:

@Transactional 注解是用来指定接口、类或方法必须拥有事务语义的元数据,一般在类上使用该注解,默认的@Transactional设置如下: 

1. 事务传播设置是 PROPAGATION_REQUIRED

2. 事务隔离级别是 ISOLATION_DEFAULT

3. 事务是 读/

4. 事务超时默认是依赖于事务系统的,或者事务超时没有被支持。

5. 任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

在服务类和方法上声明事务后,只要在bean的配置文件中作如下设置后,就可以使用事务了。

SpringQuartz

 程序中有个高级的调度需求,利用Quartz SchedulerQuartzOpenSymphony的提供任务调务类库)可以大大弥补Timer的不足可以使开发者能够完成各种复杂的任务调度)来实现。Spring Quartz提供了一些工具类,可以再Bean配置文件中配置调度任务,而不必使用Quartz API进行任何编程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值