您的位置:首页 > 编程语言 > Java开发

Spring基础常用

2012-11-19 11:24 253 查看


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代理(略)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: