spring框架基础学习

1. java 开发领域, Spring 相对于 EJB 来说是一种轻量级的,非侵入性的 Java 开发框架,曾经有两本很畅销的书《 Expert one-on-one J2EE Design and Development 》和《 Expert one-on-one J2EEdevelopment without EJB 》是 java 高手进阶必看的宝典, Spring 就是从这两本书的理论发展起来的。
Spring主要核心是:

(1).控制反转(IOC):以前传统的java开发模式中,当需要一个对象时我们,我们会自己使用new或者getInstance等直接或者间接调用构造方法创建一个对象,而在Spring开发模式中,Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。实例化一个java对象有三种方式:使用类构造器,使用静态工厂方法,使用实例工厂方法,当使用spring时我们就不需要关心通过何种方式实例化一个对象,spring通过控制反转机制自动为我们实例化一个对象。

(2).依赖注入(DI):Spring使用java Bean对象的Set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本思想。

(3).面向切面编程(AOP):在面向对象编程(OOP)思想中,我们将事物纵向抽象成一个个的对象。而在面向切面编程中,我们将一个个对象某些类似的方面横向抽象成一个切面,对这个切面进行一些如权限验证,事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。

2.在Spring中,所有管理的对象都是JavaBean对象,而BeanFactory和ApplicationContext就是spring框架的两个IOC容器现在一般使用ApplicationnContext,其不但包含了BeanFactory的作用,同时还进行更多的扩展。

3. spring配置文件bean的配置规则:

(1).一个Bean可以通过一个id属性惟一指定和引用,如果spring配置文件中有两个以上相同的id时,spring会报错id冲突。

(2).一个Bean也可以通过一个name属性来引用和指定,如果spring配置文件中有两个以上相同name的Bean,则spring通过name引用时,运行时后面的会自动覆盖前面相同name的bean引用,而不会报错。

**4.spring依赖注入3种方式:

对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式,所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程,spring的依赖注入有3种方式:
(1).使用构造器注入:
<bean id=”……” class=”……”>  
       <constructor-arg>构造函数需要的参数1</constructor-arg>  
       <constructor-arg>构造函数需要的参数2</constructor-arg>  
       …… 
</bean>  
http://static.blog.csdn.net/images/save_snippets.png使用构造器注入方式时:spring在实例化该Bean时会调用配置参数符合的构造方法。

(2).使用属性的setter注入:
<bean id=”……” class=”……”>  
      <property name=”属性1” value=”……”/>  
       <property name=”属性2” value=”……”/>  
       …… 
</bean> 
使用属性的setter注入方式时,所注入的属性必须提供setter和getter方法,spring在实例化时会自动调用无参数的构造方法或者静态工厂方法,实例化之后自动调用属性的set方法将值设置进去。

(3).使用Field字段注入方式:
该方式是spring2.5以后提供的基于java 注解(annotation)方式的注入,就是在java中的字段上或者setter方法上通过使用注解方式进行spring的依赖注入。

例如:

a.字段注入:
@Resource 
private  UserDao dao;  
b. 属性注入:
@Resource  
public void setUserDao(UserDao dao){  
       this.dao = dao; 
}  

5.spring通过注入方式注入依赖bean的方法:

Spring中为一个bean注入其依赖的另一个bean时,通过使用ref来注入另一个bean,简单例子如下:

如果bean2中通过bean1属性引用了bean1则通过下面方式注入:
<beans>  
       <bean id=”bean1” class=”……”>  
              <property name=”属性1” value=”……”/>  
              ……  
       </bean>  
<bean id=”bean2” class=”……”>  
              <property name=”bean1” ref=”bean1”/>  
              ……  
       </bean> 
</beans> 

6.spring的集合注入:

当需要给Bean的集合引用注入值时,spring也提供了相应的方法:

(1).Set集合注入:
<bean id=”……” class=”……”>  
<set>  
              <value>value1</value>  
              <value>value2</value>  
              ……  
       </set> 
</bean>   

(2).List集合注入:
          <bean  id=”……” class=”……”>  
<list>  
              <value>value1</value>  
              <value>value2</value>  
              ……  
 </list>  
           </bean>  
(3).Map集合注入:
<bean id=”……” class=”……”>  
     <map>  
              <entry key=”key1” value=”value1”>  
              <entry key=”key2” value=”value2”>  
              ……  
      </map>
</bean>  

(4).Properties注入:
<bean id=”……” class=”……”>  
     <props>  
              <prop key=”key1”>value1</prop>  
              <prop key=”key2”>value2</prop>  
              ……  
      </props>  
               </bean>  
注意:Spring会自动将其数据类型转换,支持泛型。

8. Java注解(Annotation)简单介绍:

Java注解(Annotation)是通过在java源文件上添加标记字段,然后通过反射的反射在编译或者运行时获取这些标记字段以及其标记目标,然后进行相应处理的方法,曾经风靡一时的XDoclet就是最早的java注解的工作原理说明。使用注解可以进行某些配置,大大减少xml配置文件的书写麻烦,但是也有人认为注解方式不方便理解和维护,因此对于注解VS XML配置文件一直争论不断,个人感觉没有好坏之分,合适就是最好的。

(1).JDK内置注解:

JDK内置了3个注解:

a.      @Override:声明某个方法被重写。

b.      @Deprectated:声明某个方法过时,不推荐使用。

c.      @SuppressWarning({“unchecked”,…….}):告诉编译器对于那些警告信息忽略。

(2).自定义java注解

Java注解就是一种特殊的接口,自定java注解非常方便,简单例子如下:
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.CLASS)  
public @interface TestAnnotation{  
     String value() default “”; 
}  

(3).若要想自定义的注解可以被继承,则需要在自定义注解类上加上“@Inherited”注解。

注意和说明:

a. java的注解实际上是自动继承了java.lang.annotation.Annotation接口,因此在自定义注解时不能继承其他的注解或者接口。

b. Retention:告诉编译器如何处理注解,即注解运行在什么时刻。

      RetentionPolicy是个枚举类型,有以下三种状态值:
1).SOURCE:该注解存储在源文件中,编译过后即废弃。
2).CLASS:该注解存储在class文件中,是其缺省的值。
3).RUNTIME:该注解存储在class文件中,并且有Java虚拟机读入解析,该类型非常重要,可以使用反射机制获取注解相关信息,可以进行程序分析处理

c. @Target:指定注解的目标使用对象。

ElementType也是个枚举类型,有以下几种状态值:

1).TYPE:该注解适用于class,interface,enum等类级别的目标对象。

2).FIELD:该注解适用于类中字段级别的目标对象。

3).METHOD:该注解适用于类中方法级别的目标对象。

4).PARAMETER:该注解适用于方法参数级别的目标对象。

5).CONSTRUCTOR:该注解适用于类构造方法。

6).LOCAL_VARIABLE:该注解适用于局部变量。

7).ANNOTATION_TYPE:该注解适用于annotation对象。

8).PACKAGE:该注解适用于package对象。

d.注解里面只能声明属性,不能声明方法,声明属性的方式比较特殊:

语法格式为:数据类型 属性() default 默认值(默认值是可选的); 如:Stringvalue();

使用时,“注解对象(属性=属性值)”为注解指定属性值,通过“注解对象.属性;”就可以得到注解属性的值。

e.注解的解析:使用java反射机制获得注解的目标对象就可以得到注解对象,如:

通过反射得到了注解目标的Field对象field,则通过“field.getAnnotation(注解类.class);”就可以得到注解对象。

9.   基于注解的Spring配置准备条件:

从Spring2.5以后,Spring开始全面支持注解方式配置,所以可以不用写xml配置文件,一样可以使用spring。

(1).使用spring注解方式,必须加入spring注解方式所依赖的jar包:common-annotation.jar。

(2).使用注解方式时,必须在spring配置文件的schema中添加注解的命名空间如下:
xmlns:context =”http://www.springframework.org/schema/context”  
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-2.5.xsd  

(3).在spring配置文件中注册注解处理器:

在spring配置文件中<.bean>元素之前添加:
                             <context:annotation-config>  

10.  基于注解的spring配置:

Spring2.5中使用四个注解按功能来进行对bean的配置:

(1).@Component:泛指组件,对于一般不好归类的java Bean使用。

(2).@Service:用于标注配置业务层(service层)组件。

(3).@Controller:用于标注配置控制层的组件(如Struts中的action)。

(4).@Repository:用于标注配置数据访问层组件,即一般的DAO层Bean对象。

注意:对于使用spring注解方式配置的bean对象,bean引用时默认名称为被注解名称的首字母小写形式,也可以指定名称,如:@Service(“userDao“)。


11.   Spring的自动装配:
自动装配的是指不用手动显式配置javaBean之间依赖关系,而是让spring依据某种规则自动将合适的对象注入到目标对象的过程。Spring中常用@Autowire@Resource来进行自动装配。
(1).@Autowire:默认是按照对象的数据类型进行自动装配的,如
@Autowire 
private UserDao userDao;  

spring框架在运行时会自动将类型为UserDao的对象注入进来。

(2).@Resource:默认是按照名称或者Id进行自动装配的,只有当找不到匹配的名称或者Id时才按类型进行装配,如:
@Resource(name=”userDao”) 
private UsreDao userDao;  

spring框架在运行时会将配置名称或者id为“userDao”的对象注入进来。

注意:@Autowire和@Resource都既可以写在字段上,也可以写在set方法上。

12.   Spring的自动扫描:

Spring自动扫描机制是指,我们使用基于注解的spring配置方式后,spring的配置文件中只需要注册注解处理器,不用显式地配置Bean,当spring在运行时会自动扫描根据给定的条件下类路径中的所有bean,根据注解将它们注入和装配起来并进行初始化的过程。

自动扫描很简单,只需要在spring的配置文件中添加如:

<context:component-scan base-package=“要自动扫描的包名“>  http://static.blog.csdn.net/images/save_snippets_01.png


Spring在运行时就可以对指定的包中所有添加了Spring注解的bean自动扫描,注入,装配和初始化。

注意:基于注解的spring配置,自动装配和自动扫描是紧密联系在一起协同工作的,都需要引入context的命名空间。

一、背景

       在 Spring事务:编程式事务管理这一篇中简单的介绍了Spring中的编程式事务管理,本篇介绍下Spring中声明式事务管理。

二、声明式事务管理

1、Spring中声明式事务管理概述

       Spring中的声明式事务管理在底层是建立在AOP的基础上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或加入一个事务,在执行完目标方法之后,根据执行情况再提交或回滚。

       声明式事务最大的优点是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中加入事务管理代码,只需要在配置文件中做相关的事务规则声明,或者通过等价的基于标注的方式,便可以将事务应用到业务逻辑中。

       声明式事务管理主要得益于Spring依赖注入容器和Spring AOP的支持。依赖注入容器为声明式事务管理提供了基础设施,是的bean对于Spring框架而言是可管理的;Spring AOP是声明式事务管理的直接观察者,这一点可以通过对应的配置文件得到证实。

与编程式事务管理相比,声明式唯一的不足是声明式事务管理的粒度只能作用在方法级别,无法像编程式事务那样可以作用到代码块级别。但是,这点不足之处,有很多变通的方法,比如,可以将需要事务管理的代码块单独定义成一个方法,等等。

       从绝大多数资深的开发人员的经验上看,他们更倾向于使用声明式事务管理,这是因为声明式事务管理不仅简单,而且业务代码很纯粹,不会收到污染,方便后期的维护。

 

2、基于TransactionInterceptor的声明式使馆管理

Spring提供了TransactionInterceptor类来实施声明式事务管理功能。配置文件如下:

Xml代码   收藏代码
  1. <span style="font-size: 16px;"><beans...>  
  2. ......  
  3.     <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">  
  4.         <property name="transactionManager" ref="transactionManager"/>  
  5.         <property name="transactionAttributes">  
  6.             <props>  
  7.                <prop key="transfer">PROPAGATION_REQUIRED</prop>  
  8.             </props>  
  9.         </property>  
  10.     </bean>  
  11.   
  12.     <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.origin.BankServiceImpl">  
  13.         <property name="bankDao" ref="bankDao"/>  
  14.     </bean>  
  15.       
  16.     <bean id="bankService" class="org.springframework.aop.framework.ProxyFactoryBean">  
  17.         <property name="target" ref="bankServiceTarget"/>  
  18.         <property name="interceptorNames">  
  19.            <list>  
  20.               <idref bean="transactionInterceptor"/>  
  21.            </list>  
  22.         </property>  
  23.     </bean>  
  24. ......  
  25. </beans></span>  

 

       首先,我们配置了一个TransactionInterceptor来定义相关的事务规则,它有两个主要的属性:一个是transactionManager,用来指定一个事务管理器,并将具体事务相关的操作委托给它;另一个是Properties类型的transactionAttributes属性,它主要用来定义事务规则,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符,而值就表示相应方法的所应用的事务属性。指定事务属性的取值有较复杂的规则,这在Spring中算得上是一件让人头疼的事。具体的书写规则如下:

 

描述性代码  
  1. 传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]  

 

       传播行为是唯一必须设置的属性,其他都可以忽略,Spring为我们提供了合理的默认值。传播行为的取值必须以“PROPAGATION_”开头,共七种取值,如下表所示:            

PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS

 

         隔离级别的取值必须以“ISOLATION_”开头,共五种取值,如下表所示:

ISOLATION_DEFAULT
ISOLATION_READ_COMMITTED
ISOLATION_READ_UNCOMMITTED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE

 

        如果事务是只读的,那么我们可以指定只读属性,使用“readOnly”指定。否则我们不需要设置该属性。超时属性的取值必须以“TIMEOUT_”开头,后面跟一个int类型的值,表示超时时间,单位是秒。不影响提交的异常是指,即使事务中抛出了这些类型的异常,事务任然正常提交。必须在每一个异常的名字前面加上“+”。异常的名字可以是类名的一部分。比如,

“+RuntimeException”、“+tion”等等。导致回滚的异常是指,当事务中抛出这些类型的异常时,事务将回滚。必须在每一个异常的名字前面加上“-”。异常的名字可以是类名的全部或者部分,比如“-RuntimeException”、“-tion”等等。下面给出两个实例:

 

Xml代码  
  1. <span style="font-size: 16px;"><property name="*Service">  
  2.     PROPAGATION_REQUIRED,  
  3.     ISOLATION_READ_COMMITTED,  
  4.     TIMEOUT_20,  
  5.         +AbcException,  
  6.     +DefException,  
  7.     -HijException  
  8. </property></span>  
        以上表达式表示,针对所有方法名以Service结尾的方法,使用PROPAGATION_REQUIRED事务传播行为,事务的隔离级别是ISOLATION_READ_COMMITTED,超时时间为20秒,当事务抛出AbcException或者DefException类型的异常,则仍然提交,当抛出HijException类型的异常时必须回滚事务。这里没有指定"readOnly",表示事务不是只读的。

 

Xml代码  
  1. <span style="font-size: 16px;"><property name="test">  
  2.     PROPAGATION_REQUIRED,  
  3.     readOnly  
  4. </property></span>  
       以上表达式表示,针对所有方法名为test的方法,使用PROPAGATION_REQUIRED事务传播行为,并且该事务是只读的。除此之外,其他的属性均使用默认值。比如,隔离级别和超时时间使用底层事务性资源的默认值,并且当发生未检查异常,则回滚事务,发生已检查异常则仍提交事务。

 

       配置好了TransactionInterceptor,还需要配置一个ProxyFactoryBean来组装target和advice。这是典型的Spring AOP的做法。通过ProxyFactoryBean生成的代理类就是织入了事务管理逻辑后的目标类。至此,声明式事务管理就算是实现了。我们没有对业务代码进行任何操作,所有设置均在配置文件中完成,这就是声明式事务的最大优点。

 

3、基于TransactionProxy...的声明式事务管理

       前面的声明式事务虽然好,但是却存在一个非常恼人的问题:配置文件太多。必须针对每一个目标对象配置一个ProxyFactoryBean;另外,虽然可以通过父子Bean的方式来复用TransactionInterceptor的配置,但是实际的复用几率也不高;这样,加上目标对象本身,每一个业务类可能需要对应三个 <bean/> 配置,随着业务类的增多,配置文件将会变得越来越庞大,管理配置文件又成了问题。

       为了缓解这个问题,Spring提供了TransactionProxyFactoryBean,用于将TransactionInterceptor 和ProxyFactoryBean的配置合二为一。如下面的代码所示:

Xml代码  
  1. <span style="font-size: 16px;"><beans......>  
  2.     ......  
  3.     <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.classic.BankServiceImpl">  
  4.        <property name="bankDao" ref="bankDao"/>  
  5.     </bean>  
  6.     <bean id="bankService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
  7.        <property name="target" ref="bankServiceTarget"/>  
  8.        <property name="transactionManager" ref="transactionManager"/>  
  9.        <property name="transactionAttributes">  
  10.            <props>  
  11.              <prop key="transfer">PROPAGATION_REQUIRED</prop>  
  12.            </props>  
  13.        </property>  
  14.     </bean>  
  15.     ......  
  16. </beans></span>  
       如此一来,配置文件与先前相比简化了很多,这种配置方式称为Spring经典的声明式事务管理。但是,显式为每一个业务类配置一个TransactionProxyFactoryBean的做法将使得代码显得过于刻板,为此可以使用自动创建代理的方式来将其简化,使用自动创建代理是纯 AOP 知识,请读者参考相关文档。

 

4、基于<tx>命名空间的声明式事务管理

      前面两种声明式事务配置方式奠定了Spring声明式事务管理的基石。在此基础上,Spring 2.x 引入了<tx>命名空间,结合使用<aop>命名空间,带给开发人员配置声明式事务的全新体验,配置变得更加简单和灵活。另外,得益于 <aop> 命名空间的切点表达式支持,声明式事务也变得更加强大。如下面的配置文件所示:

Xml代码  
  1. <span style="font-size: 16px;"><beans......>  
  2.     ......  
  3.     <bean id="bankService"  class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">  
  4.        <property name="bankDao" ref="bankDao"/>  
  5.     </bean>  
  6.     <tx:advice id="bankAdvice" transaction-manager="transactionManager">  
  7.        <tx:attributes>  
  8.           <tx:method name="transfer" propagation="REQUIRED"/>  
  9.        </tx:attributes>  
  10.     </tx:advice>  
  11.   
  12.     <aop:config>  
  13.        <aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/>  
  14.        <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>  
  15.     </aop:config>  
  16.     ......  
  17. </beans></span>  
 
如果默认的事务属性就能满足要求,那么代码简化为如下所示:
Xml代码  
  1. <span style="font-size: 16px;"><beans......>  
  2.     ......  
  3.     <bean id="bankService" class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">  
  4.        <property name="bankDao" ref="bankDao"/>  
  5.     </bean>  
  6.     <tx:advice id="bankAdvice" transaction-manager="transactionManager">  
  7.     <aop:config>  
  8.        <aop:pointcut id="bankPointcut" expression="execution(**.transfer(..))"/>  
  9.        <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>  
  10.     </aop:config>  
  11.     ......  
  12. </beans></span>  
       由于使用了切点表达式,我们就不需要针对每一个业务类创建一个代理对象了。另外,如果配置的事务管理器Bean的名字取值为“transactionManager”,则可以省略 <tx:advice> 的transaction-manager属性,因为该属性的默认值即为“transactionManager”。

 

5、基于@Transactional的声明式事务管理

       除了基于命名空间的事务配置方式,Spring 2.x 还引入了基于Annotation的方式,具体主要涉及@Transactional标注。@Transactional可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有public方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如下面的代码所示:

 

Xml代码  
  1. <span style="font-size: 16px;">@Transactional(propagation = Propagation.REQUIRED)  
  2. public boolean transfer(Long fromId, Long toId, double amount) {  
  3.     return bankDao.transfer(fromId, toId, amount);  
  4. }</span>   
       Spring使用BeanPostProcessor来处理Bean中的标注,因此我们需要在配置文件中作如下声明来激活该后处理Bean,如下面代码所示:
Xml代码  
  1. <span style="font-size: 16px;"><tx:annotation-driven transaction-manager="transactionManager"/></span>  

 

       与前面相似,transaction-manager属性的默认值是transactionManager,如果事务管理器Bean的名字即为该值,则可以省略该属性。

       虽然@Transactional注解可以作用于接口、接口方法、类以及类方法上,但是Spring小组建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional注解应该只被应用到public方法上,这是由Spring AOP的本质决定的。如果你在protected、private或者默认可见性的方法上使用@Transactional 注解,这将被忽略,也不会抛出任何异常。

       基于<tx>命名空间和基于@Transactional的事务声明方式各有优缺点:

       基于<tx>的方式,其优点是与切点表达式结合,功能强大。利用切点表达式,一个配置可以匹配多个方法。

       基于@Transactional的方式必须在每一个需要使用事务的方法或者类上用@Transactional标注,尽管可能大多数事务的规则是一致的,但是对@Transactional而言,也无法重用,必须逐个指定。另一方面,基于@Transactional的方式使用起来非常简单明了,没有学习成本。开发人员可以根据需要,任选其中一种使用,甚至也可以根据需要混合使用这两种方式。

       如果不是对遗留代码进行维护,则不建议再使用基于TransactionInterceptor以及基于TransactionProxyFactoryBean的声明式事务管理方式,但是,学习这两种方式非常有利于对底层实现的理解。

       虽然上面共列举了四种声明式事务管理方式,但是这样的划分只是为了便于理解,其实后台的实现方式是一样的,只是用户使用的方式不同而已。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值