现在的位置: 首页 > 综合 > 正文

Spring基础常用 指引

2013年12月12日 ⁄ 综合 ⁄ 共 10263字 ⁄ 字号 评论关闭

//2008-04-02 09:12

Spring基础常用 指引

Spring的是建立在IOC和AOP概念之上的一个框架,所以先简要介绍一下这两个概念:

IOC: Inversion of Control (控制反转) 的意思呢: 就是把要在程序中实例化的对象配置到文件中,在程序中不用new来产生,而是让容器通过配置文件返回一个给你,好处就是当需要更改业务逻辑时,方便替换对接口的不同实现类,也就是Dependency Injection(依赖注入).
AOP: Aspect Oriented Programming (面向切面编程) 用来在系统中提升业务的分离,把日志,安全,事务等东西和核心业务分离开,甚至核心业务都不知道它们的存在. 基本实现就是对相关的方法进行拦截,添加所需的处理动作.

在Spring中对这两项功能的使用是通过配置xml文件来实现的,配置你实现IOC的类和想添加AOP特性的方法.

没有了new,想从配置文件中得到配置的类,就需要有一个容器,容器是Spring框架的核心.
Spring有两种容器: <1>Bean工厂(org.springframework.beans.factory.BeanFactory接口定义)是最简单的容器,提供了基础的依赖注入支持. <2>应用上下文(org.springframework.context.ApplicationContext接口定义)建立在Bean工厂基础之上,提供更多的系统构架服务,如:资源,消息,事件.

-----------------------------

AOP简介:

AOP有很多术语,会有一部分反映到配置文件中,一部分是一个整体的概念,所以必须搞明白些.

<I> Aspect(切面):切面是你要实现的交叉功能,如日志记录,安全控制,事务管理等. (个人感觉是个大概念,下面几个小概念组合在一起即是它)

<II> Joinpoint(连接点): 连接点是应用程序执行过程中插入Aspect的地点.这个地点可以是方法调用,异常抛出等时刻.(概念因素多一些)

<III> Advice(通知): 通知为Aspect的具体实现.(有具体实物类,即继承了org.aopalliance.aop.Advice的接口类)

<IV> PointCut(切入点): 切入点定义了Advice应该应用在哪些Joinpoint.(有具体实物类,即继承了org.springframework.aop.Pointcut的接口类)

<V> Introduction(引入): 引入允许你为已存在的类添加新的方法和属性. (有具体实物类,即继承了org.aopalliance.intercept.Interceptor的接口类,其实Interceptor继承了Advice的接口,也可看作是一种Advice)

<VI> Target(目标对象): 实施Advice的目标.可以为你编写的类,也可为你要添加定制行为的第三方类.(有具体实物类)

<VII> Proxy(代理): 代理是将Advice应用到Target后所创建的对象. 表面上和Target对象用法一样,只是被容器包装过. (spring将自动实现,分两种代理创建方式:<1>如果Target实现了一个(或多个)接口暴露的方法,Spring使用JDK的java.lang.reflect.Proxy类创建代理,这个类让Spring动态产生一个新的类,它实现了所需的接口,织入了Advice,并代理对Target的所有请求. <2>如果目标对象没有实现任何接口,Spring使用CGLIB库生成Target的子类(注意final方法不能被通知),在创建这个子类的时候,Spring将Advice织入,并将对目标对象的调用委托给这个子类.) (现实应用程序中应该得到和使用由容器产生的这个proxy对象,如:ProxyFactoryBean)

<VIII> Weaving(织入): 织入是将Aspect应用到Target从而创建一个新的Proxy对象的过程.织入可以发生在目标对象的生命周期的多个点上,分为: 编译期织入;类装载期织入;运行期织入(Spring使用).

也许这么多概念都把人搞迷糊了. Advice包括需要应用的交叉行为,Joinpoint是Advice要在应用系统需要应用的所有PointCut,PointCut定义了Advice要在哪些JoinPoint应用.在此最关键的概念是什么呢? PointCut定义了Advice要在哪些JoinPoint应用.

定义PointCut切入点的原因: 如果我们不能表达在应用系统的什么地方应用这些Advice的话,编写的Advice毫无用处,这就是切入点的用处.

下面来看看Pointcut的接口:
public interface Pointcut{
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}

Pointcut是根据方法和类决定在什么地方织入(weaving)通知(Advice). ClassFilter接口决定了一个类是否符合通知(Advice)的要求.

ClassFilter接口:
public interface ClassFilter{
boolean matches(Class class);
}

虽然ClassFilter让你通过类过滤切面,如果你同时需要方法的过滤,则通过MethodMatcher接口可以实现这个功能:
public interface MethodMatcher{
public boolean matches(Method m,Class targetClass);
public boolean isRuntime();
public boolean matches(Method m,Class target,Object[] args);
}

现在知道了定义切点(PointCut)的内部结构,但是在实际应用中,会经常使用spring预定义的切入点实现.

大多数切面(Aspect)是由定义切面行为的通知(Advice)和定义切面在什么地方执行的切入点(Pointcut)组合而成,所以spring提供了Advisor,它把通知(Advice)和切入点(Pointcut)组合在一个对象中.如下:

public interface PointcutAdvisor{
Pointcut getPointCut();
Advice getAdvice();
}

应用中我们大多数都会使用Spring自带的切入点,大多数Spring自带的切入点都会有一个对应的PointcutAdvisor.方便你在一个地方定义切入点(Pointcut)和通知(Advice).

Spring提供的切入点分静态和动态两种.主要介绍静态的.

静态切入点:NameMatchMethodPointcut类,这个类你只需关注public void setMappedName(String)和public void setMappedNames(String[])两个方法即可.

简单配置文件如下:

(目标Target)
<bean id="mailTarget" class="xxx.xxx.mailTargetImpl" />

(实现的通知Advice)
<bean id="customAdvice" class="xxx.xxx.cusetAdvice" />

(配置PointcutAdvisor)
<bean id="customerPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedNames">
<list>
<value>aMethodName</value>
<value>endMatch*</vaule>
<list>
</property>
<property name="advice"> <ref bean="customAdvice"/> </property>
</bean>

(配置代理Proxy)
<bean id="mailService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"> <value> xxx.xxx.interface.mailService </value> </property>
<property name="interceptorNames"> <list> <value> customerPointcutAdvisor </value> </list> </property>
<property name="target"> <value ref="mailTarget" /> </property>
</bean>

Spring的RegexpMethodPointcut可以利用正则表达式来定义切入点.

动态切入点的类是ControlFlowPointcut.

-----------------------------

以下是Spring配置文件的一些常用项,一一列出来.(property中的name其实就是class类中的一个field,通过反射set方法向里面赋值)

<1>配置DataSource
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url"> <value> jdbc:mysql://192.168.0.1:3306/someDB?characterEncoding=utf-8 </value> </property>
<property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property>
<property name="username"><value>${db.dbuser}</value></property>
<property name="password"><value>${db.password}</value></property>
</bean>
${variable}符号可以从装载的property文件中取值.

(2) 装载Property配置文件
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value> jdbc.properties </value>
</property>
</bean>

(3)文本国际化,使用方法是applicationContext.getMessage("key");
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"><value>messageFileName</value></property>
</bean>

文件结尾按地区分类:messageFileName_en_US.properies,messageFileName_zh_CN.properies,默认不加区位:messageFileName.properies.

-----------------------------

Spring与数据库部分整合,写dataSource配置,用模板,应用起来不算复杂,忽略...

-----------------------------

关于spring事务管理:

Atomic(原子性) Consistent(一致性) Isolated(隔离性) Durable(持久性)

Spring提供 '程序控制式' 和 '声明式' 两种事务管理方法.

spring没有直接管理事务,而提供很多供选择的事务管理器:
单一的JDBC: org.springframework.jdbc.datasource.DataSourceTransactionManager
Hibernate: org.springframework.orm.hibernate.HibernateTransactionManager

加入到xml应用上下文:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"> <ref bean="dataSource"> </property>
</bean>

<一>手动程序控制事务:

一个方法是使用Spring的TransactionTemple类,用程序添加事务边界,利用一套回调机制.
public void enrollStudentInCourse()
{
transactionTemple.execute(
new TransactionCallback(){
public Object doInTransaction()
{
try{
//do stuff
}catch(Exception e)
{
ts.setRollbackOnly();
}
return null; //如果成功,事务被提交
}//try
}//public
);
}

使用的同时要配置相关文件,将transactionTemple注入到应用类中,如下

<bean id="transactionTemple" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager"> <ref bean="transactionManager" /> </property>
</bean>

<bean id="courseService" class="xxx.xxx.CourseServiceImpl">
<property name="transactionTemplate"> <ref bean="transactionTemple" /> </property>
</bean>

<二>声明式事务
要使用声明式事务,必须得使用TransactionProxyFactoryBean.配置如下:

<bean id="courseService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyInterfaces"> <list> <value> xxx.xxx.CourseService </value> </list> </property>
<property name="target"> <ref bean="courseServiceTarget" /> </property>
<property name="transactionManager"> <ref bean="transactionManager" /> </property>
<property name="transactionAttributeSource"> <ref bean="attributeSource" /> </property>
</bean>

其中有一项是transactionAttributeSource(事务属性源),这是什么意思呢?

------

事务属性 是对事务策略如何应用到方法的描述,这个描述包括以下一个或多个参数: 1)传播行为 2)隔离级别 3)只读提示 4)事务超时间隔

1)传播行为 定义了关于客户端和被调用方法的事务边界.Spring定义了7种传播行为:

PROPAGATION_MANDATORY 表示该方法必须运行在一个事务中. 如果当前事务不存在,将抛出一个异常.
PROPAGATION_NESTED 表示如果当前已经存在一个事务,那该方法应当运行在一个嵌套的事务中.被嵌套的事务可以从当前事务中单独的提交或回滚.如果当前事务不存在,那么它看起来就和PROPAGATION_REQUEST没有两样.
PROPAGATION_NEVER 表示当前的方法不应该运行在一个事务上下文中,如果当前存在一个事务,则会抛出一个异常.
PROPAGATION_NOT_SUPPORTED 表示该方法不应在事务中运行.如果一个现有的事务正在运行中,它将在该方法的运行期间被挂起.
PROPAGATION_REQUIRED 表示当前方法必须运行在一个事务中,如果一个现有的事务正在进行中,该方法将运行在这个事务中.否则,就要开始一个新的事务.
PROPAGATION_REQUIRES_NEW 表示当前方法必须运行在它自己的事务里.它将启动一个新的事务.如果一个现有的事务在运行的话,将在这个方法运行期间被挂起.
PROPAGATION_SUPPORTS 表示当前方法不需要事务处理环境,当如果有一个事务已经在运行的话,这个方法也可以在这个事务里运行.

2)隔离级别

脏读(Dirty read):脏读发生在一个事务读取了被另一个事务改写但还未提交的数据时.如果这些改变在稍后被回滚,那么第一个事务读取的数据就是无效的.
不可重复读(Nonrepeatable read):不可重复读发生在一个事务执行相同的查询两次或两次以上,但每一次查询结果都不相同.这通常是由于另一个并发事务在两次查询之间更新了数据.
幻读(Phantom read):幻读和不可重复读相似.当一个事务(T1)读取几行纪录后,另一个并发事务(T2)插入一些数据,幻读就发生了.在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外纪录.

ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 允许你读取还未提交的改变了的数据.可能导致脏读,幻读或不可重复读.
ISOLATION_READ_COMMITTED 允许在并发事务已经提交后读取,可以防止脏读,但幻读和不可重复读仍可能发生.
ISOLATION_REPEATABLE_READ 对相同字段的多次读取结果是一致的,除非数据被事务本身改变.可防止脏读和不可重复读,但幻读仍可能发生.
ISOLATION_SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏读,不可重复读和幻读.这在所有隔离级别中也是最慢的,因为它是典型的通过完全锁定在事务中涉及的数据表来完成的.

3)只读 readOnly
如果一个事务只对后端数据库执行读操作,数据库就可能利用事务只读的特性,使用某些优化措施.

4)事务超时

其中3)4)两项只有对具有可能启动新事务的传播行为才有意义(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED)

------

TransactionAttribute接口如下:
public interface TransactionAttributeSource{
public TransactionAttribute getTransactionAttribute(java.lang.reflect.Method,java.lang.Class targetClass);
}

TransactionAttribute接口实现类:

(1)MatchAlwaysTransactionAttributeSource可能是最简单的实现类,不管这个事务中包装了哪个方法,默认为PROPAGATION_REQUIRED和ISOLATION_DEFAULT.

如果想让MatchAlwaysTransactionAttributeSource返回其他的事务属性,则可以这样配置:

<bean id="myTransactionAttribute" class="org.springframework.transaction.interceptor.DefaultTransactionAttribute">
<property name="propagationBehaviorName"><value>PROPAGATION_REQUIRES_NEW</value></property>
<property name="isolationLevelName"><value>ISOLATION_REPEATABLE_READ</value></property>
</bean>

<bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
<property name="transactionAtrribute"><ref bean="myTransactionAttribute"></property>
</bean>

(2)通过方法名声明事务,可以将事务管理控制在方法名称上,使用类为NameMatchTransactionAttributeSource:

对于方法的事务属性描述,格式如下:
PROPAGATION(传播行为),ISOLATION(隔离级别,可选),readonly(是否是只读事务,可选),-Exception,+Exception(回滚规则,可选,负号为异常发生时回滚,正号为即使这个异常抛出,仍可以提交)

此类配置使用方法如下:
<bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="someMethodName">
PROPAGATION_REQUIRES_NEW,ISOLATION_REPEATABLE_READ,readOnly,-Exception
</prop>
<prop key="endWithSome*"> PROPAGATION_REQUIRED </prop>
</props>
</property>
</bean>

------

优化事务声明配置文件

当配置了多个事务时,你会发现几个对象的声明,其实都是TransactionProxyFactory的实例,只是Target名称不同.
事实上Spring提供两种方法抗击这个复杂的XML: 1)Bean继承 2)AOP代理

1)Bean继承:
使用<bean>标签的parent属性,就成父bean的属性.如下:

<bean id="abstractTxDefinition" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true">
<property name="transactionManager"> <ref bean="transactionManager" /> </property>
<property name="transactionAttributeSource"> <ref bean="attributeSource" /> </property>
</bean>

<bean id="studentService" parent="abstractTxDefinition">
<property name="target"> <bean class="xxx.xxx.studentServiceImpl"/> </property>
</bean>

2)AOP代理(略)

-----------------------------

参考书目:
<Spring 技术手册> -- 快速入门,快速上手应用,组织有些混乱,不求甚解,入门专用.
<Spring in Action> -- 徐徐渐进讲的有条理很清楚,一步一步,为什么,怎么做,好书.

XO:本想把结构组织好一点,结果用txt打出来无法排版,自己也太懒,就把常用的部分写出来,比较乱,其实是给我自己查找用的.没讲清楚的,有需要的人还是去看这两本书吧.还有一本书叫<Professional Java Development with the Spring Framework (SPRING框架高级编程),Rod Johnson等著,机械工业出版社>,很不错,很不错.

//2008-04-03 15:18

抱歉!评论已关闭.