Spring框架

一、Spring介绍

Spring是一个开源框架,能够大大的简化企业级应用开发的复杂性;使用Spring,我们就可以用简单的JavaBean来实现以前EJB才能实现的功能;Spring给企业级应用提供了很多功能;总体来说Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架。

 

1>轻量级:                                              

从大小和应用开支上来说Spring都是轻量级的,整个Spring框架就可以打包成一个3MB左右的jar包,并且Spring的处理开支也非常小;                        

更重要的是,Spring是非侵入式的,基于Spring开发的应用程序中的对象一般无需依赖于Spring的API;

2>控制反转(或依赖注入):                                         

Spring提供的一种松耦合的技术。使用控制反转(依赖注入),对象是被动接收依赖对象而不是自己主动去找。即对象不是从容器中查找它的依赖对象,而是容器在实例化对象时主动将它的依赖对象注入给它。

3>面向切面:                                            

Spring对面向切面编程提供了强大支持。通过将业务逻辑从应用服务(如监控和事务管理)中分离出来,应用对象只做它们该做的业务逻辑,不负责和关心其系统问题(如日志和事务支持)。

4>容器:                                                 

Spring框架本身就是一个容器,因为它包含并且管理应用对象的生命周期和配置。我们可以通过配置来设定Bean是单一实例,还是每次请求产生一个实例,并且设定它们之间的关联关系。以此替代了传统的重量级的EJB容器。

5>框架:                                                 

Spring实现了使用简单的组件配置组合成一个复杂的应用。在Spring中,应用中的对象是通过XML文件配置或注解组合起来的。并且Spring提供了很多基础功能,如事务管理、持久层集成等,这使开发人员只需要于专注于开发应用逻辑。

6>一站式:                                               

Spring在DI和AOP的基础上可以整合各种企业应用的开源框架和第三方类库。

 

二、Spring模块结构:

 

 

Spring可以作为企业级应用程序的一站式服务点,然而,Spring是模块化的,在Spring4.X中提供了约20个模块,我们可以根据应用程序的需要灵活的来挑选对应模块使用,不必要把不需要的模块也引入到应用程序中,且每个模块的实现都有其所依赖的jar包。

1.核心容器(Core Container):                                  

   由Core、Beans、Context、SpEL 4个模块组成

1>核心模块(Core):                                            

    提供了Spring框架的基本组成部分,主要包括IOC(反转控制)和DI(依赖注入)功能。

    依赖jar包:spring-core.jar、commons-logging.jar

2>Beans模块:                                                

    提供BeanFactory和Bean的装配。

    依赖jar包:spring-beans.jar和spring-core.jar

3>上下文模块(Context):                                   

    spring的Context上下文即IOC容器,其建立在核心模块和Beans模块的基础上,它是定义和配置对象的媒介。ApplicationContext接口是上下文模块的重点。

    依赖jar包:spring-context.jar、spring-core.jar、spring-beans.jar、spring-aop.jar、 spring-expression.jar

4>spring表达式语言模块(SpEL):                                   

   提供了查询和操作对象图的强大的表达式语言。

   依赖jar包:spring-expression.jar、spring-core.jar

2.数据访问/集成(Data Access/Integration):                     

   包括JDBC、ORM、OXM、JMS和事务处理模块。

1>JDBC模块:

    JDBC的支持。

    依赖jar包:spring-jdbc.jar、spring-core.jar、spring-beans.jar、spring-tx.jar

2>ORM模块:

    对象关系映射,集成ORM框架。

    依赖jar包:spring-orm.jar、spring-core.jar、spring-beans.jar、spring-jdbc.jar、spring-tx.jar                     

3>OXM模块:

    对象XML映射的实现。

    依赖jar包:spring-oxm.jar、spring-core.jar、spring-beans.jar

4>JMS模块:

    Java消息服务。

    依赖jar包:spring-jms.jar、spring-core.jar、spring-beans.jar、spring-aop.jar、spring-tx.jar、spring-context.jar

5>事务模块(Transactions):

    事务处理。

    依赖jar包:spring-tx.jar、spring-core.jar、spring-beans.jar

3.Web部分:                                                        

   由Web、Web-MVC、Web-Socket和Web-Portlet四个模块组成

1>Web模块: 

    提供了基本的面向web的集成功能,如文件上传功能、使用Servlet监听器和面向web应用程序的上下文来初始化IOC容器。

    依赖jar包:spring-web.jar、spring-core.jar、spring-beans.jar、spring-aop.jar、spring-context.jar

2>Web-MVC模块:

   提供了Spring的MVC。

   依赖jar包:spring-webmvc.jar、spring-core.jar、spring-beans.jar、spring-web.jar、spring-expression.jar、spring-context.jar

3>Web-Socket模块:

   为web应用程序提供的高效通信工具。

   依赖jar包:spring-websocket.jar、spring-core.jar、spring-web.jar、spring-context.jar

4>Web-Portlet模块:

   提供了基于portlet的MVC的实现,并反映了Web-Servlet模块的功能。

   依赖jar包:spring-webmvc-portlet.jar、spring-core.jar、spring-beans.jar、spring-web.jar、spring-webmvc.jar、spring-context.jar

4.AOP部分:

  1>AOP模块:

    提供了面向切面编程。允许我们定义方法拦截器和切入点,对代码进行干净的解耦,实现代码分离功能。

    依赖jar包:spring-aop.jar、spring-core.jar、spring-beans.jar、aopalliance.jar

  2>Aspects模块:

    提供了与AspectJ的集成,这是一个功能强大且成熟的面向切面编程框架。

    依赖jar包:spring-aspects.jar、org.aspectj.jar

5.测试模块(Test)

  依赖jar包:spring-test.jar、spring-core.jar

6.其它模块:

1>Instrumentation模块:

    在应用服务器中提供了类Instrumentation的支持和类加载器的实现。

    依赖jar包:spring-instrument.jar

2>Messaging模块:

    用于构建基于消息的应用程序。

    依赖jar包:spring-messaging.jar、spring-core.jar、spring-beans.jar、spring-context.jar

3>Support模块:

    spring额外支持包,如邮件服务、视图解析等。

    依赖jar包:spring-context-support.jar、spring-core.jar、spring-beans.jar、spring-context.jar

 

三、Spring IOC

控制反转(Inversion Of Control)。                               

其思想是反转资源获取的方向。传统的资源获取方式是组件向容器发起获取资源的请求,作为回应,容器适时的向组件返回资源;而应用了IOC之后,则是容器主动将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接收资源,这种行为也被称为查找的被动形式。

将IOC应用到程序中,就是组件本身不再负责依赖对象的创建和维护,依赖对象的创建和维护是由IOC容器负责的,组件只负责接收IOC容器创建的依赖对象。这样控制权就由组件转移到了IOC容器,控制权的转移就是所谓的反转。

 

四、依赖注入(Dependency Injection):

依赖注入和控制反转其实是一回事,只是IOC的另一种表述方式。      

依赖注入就是组件以一些预先定义好的方式(如,set方法、构造器等)接收来自于容器的资源注入。即在程序运行时,由IOC容器动态的将创建好的依赖对象,以特定的方式注入到组件中。

 

五、Spring AOP:

1.Spring AOP介绍:                                   

AOP(Aspect-Oriented Programming面向切面编程)。                     

是一种新的方法论,是对面向对象编程(OOP)的补充。面向对象引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过面向对象只允许开发者定义纵向的关系,但并不适合定义横向的关系。例如日志功能,日志代码往往横向地散布在所有对象层次中,而它与对应的对象的核心功能是毫无关系的,这种散布在各处的无关的代码被称为横切,在面向对象设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术恰恰相反,它的主要编程对象是切面。它剖解开封装的对象内部,并将那些影响了多个类的公共的无关行为封装到一个可重用模块,并将其命名为切面。所谓切面,简单说就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于代码后期的可操作性和可维护性。

AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的特点是,它经常发生在核心关注点的多处,而各处基本都相似,比如日志功能。AOP的作用在于分离系统中的核心关注点和横切关注点。

AOP的底层实现就是动态代理,但是却简化了动态代理的操作以及大大降低了开发人员的代码量。

 

2.AOP术语:

1>通知:描述了切面所要执行的内容,以及决定了何时执行切面的内容。                        

2>连接点:连接点是程序执行过程中能够插入切面的点,连接点可以是调用方法时、抛出异常时、甚至修改字段时,切面代码可以利用这些点插入到程序的正常流程中;程序执行过程中能够应用通知的所有连接点。

3>切点:如果通知定义了"什么"和"何时",那么切点就定义了在"何处"。切点会匹配通知所要织入的一个或多个连接点。通常使用明确的类或者方法来指定这些切点。总之切点就定义了通知被应用的位置(在哪些连接点)。

4>切面:切面是通知和切点的集合,通知和切点共同定义了切面的全部功能,它是什么,在何时何处完成其功能。

5>目标:被代理的目标对象

6>代理:向被代理的目标对象应用了切面之后所创建的代理对象

 

3.Spring AOP的实现:

1>基于AspectJ注解的方式实现AOP:

1)在Spring中启用AspectJ的注解支持:                           

要在Spring应用中使用AspectJ的注解,必须导入支持AspectJ框架的jar包:com.springsource.org.aopalliance.jar 、com.springsource.org.aspectj.weaver.RELEASE.jar 、spring-aspects.RELEASE.jar

2)用AspectJ的注解声明切面:                                   

使用AspectJ的注解声明的切面其实就是一个带有@Aspect注解的类;  

通知就是标注有特定注解的切面类中定义的方法,它定义了切面执行的内容以及决定何时执行切面的内容。常见的通知注解有:

@Before:前置通知,在目标的指定方法执行之前执行通知方法。                                                                                                                                                                                                       

@After:后置通知,在目标的指定方法执行之后执行通知方法,无论目标方法执行是否有异常,不能访问目标方法的返回值。                                                                                               

@AfterReturning:返回通知,在目标的指定方法正常执行且返回结果后执行通知方法,能访问目标方法的返回值。                                                                                                                   

@AfterThrowing:异常通知,在目标的指定方法抛出异常之后执行通知方法,也不能访问目标方法的返回值。                                                                                                                             

@Around:环绕通知,围绕着目标的指定方法执行通知方法,体现动态代理的全过程。

切点在通知注解中使用切点表达式作为通知注解的value参数值来表示,指定了通知方法被应用的位置。

3)在Bean配置文件中引入aop schema约束,定义一个<aop:aspectj-autoproxy>标签。Spring IOC容器在初始化后,会自动创建在Bean配置文件中配置的所有bean实例以及切面类的bean实例。然后当Spring IOC容器扫描到<aop:aspectj-autoproxy>标签,还会自动为那些与声明的切面相匹配的目标bean创建代理对象。

 

2>基于xml配置的方式实现AOP: 

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

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

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

   xmlns:aop="http://www.springframework.org/schema/aop"

   xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

   http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

 

    <!-- 

     配置MathImpl类的bean实例

    -->

    <bean id="math" class="com.mmy.aop.xml.MathImpl"></bean>

    <!-- 

     配置切面类LogAspect的bean实例

    -->

    <bean id="log" class="com.mmy.aop.xml.LogAspect"></bean>

    <!--

      使用<aop:config>标签配置AOP:   

    -->

    <aop:config>

       <!--

         使用<aop:config>标签的<aop:pointcut>子标签配置切入点,

         expression属性指定切入点表达式,id属性给切入点命名

       -->

       <aop:pointcut expression="execution(* com.mmy.aop.xml.MathInterface.*(..))" id="cutPoint"/>    

       <!--

        使用<aop:config>标签的<aop:aspect>子标签配置切面,ref属性

        指定切面bean实例

       -->

       <aop:aspect ref="log">

          <!--

           使用<aop:aspect>标签的<aop:before>子标签给切面配置前置通知,

           method属性指定通知方法,pointcut-ref属性指定切入点

          -->

          <aop:before method="beforeMethod" pointcut-ref="cutPoint"/>

          <!--

           使用<aop:aspect>标签的<aop:after>子标签给切面配置后置通知,

           method属性指定通知方法,pointcut-ref属性指定切入点

          -->

          <aop:after method="afterMethod" pointcut-ref="cutPoint"/>

          <!--

           使用<aop:aspect>标签的<aop:after-returning>子标签给切面

           配置返回通知,method属性指定通知方法,pointcut-ref属性指定切入点,

           returning指定连接点方法的返回值名称

          -->

          <aop:after-returning method="returnMethod" pointcut-ref="cutPoint" returning="result"/>

          <!--

           使用<aop:aspect>标签的<aop:after-throwing>子标签给切面

           配置异常通知,method属性指定通知方法,pointcut-ref属性指定切入点,

           throwing属性指定连接点方法抛出的异常对象名

          -->

          <aop:after-throwing method="throwMethod" pointcut-ref="cutPoint" throwing="e"/>

       </aop:aspect>    

     </aop:config>

</beans>

4.其它

1>利用方法签名编写切入点表达式:

1)execution(* com.mmy.aop.aspect.MathInterface.*(..)):                

匹配com.mmy.aop.aspect.MathInterface中声明的所有方法。第一个*表示任意修饰符及任意返回值类型,第二个*表示任意方法,..表示任意类型和个数的参数。若目标所属类或接口与切面类在同一个包中,可以省略包名。

2)execution(public * com.mmy.aop.aspect.MathInterface.*(..)):

匹配com.mmy.aop.aspect.MathInterface中声明的修饰符是public的所有方法。第一个*表示任意返回值类型,第二个*表示任意方法,..表示任意类型和个数的参数。

3)execution(public int com.mmy.aop.aspect.MathInterface.*(..)):

匹配com.mmy.aop.aspect.MathInterface中声明的修饰符是public返回值类型是int的所有方法。第一个*表示任意方法,..表示任意类型和个数的参数。

4)execution(public int com.mmy.aop.aspect.MathInterface.*(int,..)):

匹配com.mmy.aop.aspect.MathInterface中声明的修饰符是public返回值类型是int第一个参数是int类型的所有方法。第一个*表示任意方法,..表示第一个int型参数之后的任意类型和个数的参数。

5)execution(public int com.mmy.aop.aspect.MathInterface.*(int,double)):

匹配com.mmy.aop.aspect.MathInterface中声明的修饰符是public返回值类型是int第一个参数是int类型第二个参数是double类型的所有方法。第一个*表示所有方法。

2>连接点对象:                                                 

在通知方法中可以传入一个org.aspectj.lang.JoinPoint类型的参数,即连接点对象。通过连接点对象在通知方法中就可以获取到目标中连接点的相关信息,如连接点方法名和参数值等。

JoinPoint对象常用方法:

Signature | getSignature():获取连接点方法的方法签名对象

Object[] | getArgs():获取连接点方法中的所有参数

Object | getTarget():获取连接点所在的目标对象

Object | getThis():获取代理对象本身  

环绕通知方法必须携带org.aspectj.lang. JoinPoint接口的子接口org.aspectj.lang.ProceedingJoinPoint类型的对象参数,该参数对象同样表示目标的连接点。ProceedingJoinPoint对象的proceed()方法用于执行目标的连接点方法。

3>切面的优先级:

当在同一个目标的连接点上应用了多个切面时,可以通过在这多个切面类上标注@Order注解来设置切面的优先级,给@Order注解的value参数赋予一个非负整数,其值越小切面的优先级越高。

4>切入点表达式的复用:                                        

在定义切面类时,一般是在通知方法中使用通知注解的value参数指定切入点表达式,来指定通知所应用的连接点方法的。但是同一个切入点表达式可能会在多个通知中重复使用,那么就可以实现切入点表达式的复用。

只需要在切面类中定义一个被@PointCut注解标识的无参无方法体返回值类型为void的方法,再使用@PointCut注解的value参数指定重复使用的切入点表达式,这类方法一般被称为切入点方法。那么在通知方法的通知注解中就可以使用参数value调用切入点方法来引入切入点表达式。

通过设置切入点方法的修饰符可以设置其使用范围。如果切入点方法只希望在本切面类中使用,可以将其修饰符设置为private;如果切入点方法还希望在其它切面类中使用,可以将其修饰符设置为public;如果其它切面类和定义切入点方法的切面类在同一包中,那么调用切入点方法时只需要使用切面类名.切入点方法,如果其它切面类和定义切入点方法的切面类不在同一包中,那么调用切入点方法时需要使用包名.切面类名.切入点方法。

 六、Spring API 

1.BeanFactory:

BeanFactory是 Spring 的"心脏"。

BeanFactory是 IOC 容器的核心接口,它定义了IOC的基本功能。

Spring使用它来配置文档,管理bean的加载,实例化并维护bean之间的依赖关系,负责bean的生命周期。

 

2.ApplicationContext:

ApplicationContext由BeanFactory派生而来,可以比喻为Spring的躯体。

ApplicationContext在BeanFactory的基础上添加了很多功能:

     支持了aop功能和web应用 MessageSource, 提供国际化的消息访问

     通过配置来实现BeanFactory中很多编码才能实现的功能

ApplicationContext的常用实现类 ClassPathXmlApplicationContext:从classpath目录读取配置文件

FileSystemXmlApplicationContext:从文件系统或者url中读取配置文件

AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 Spring 容器。它用来读取注解。

 

3.BeanFactory和ApplicationContext的区别:

beanFactory主要是面向Spring框架的基础设施,也就是供Spring自身内部调用, 而Applicationcontext 主要面向Spring的使用者。

BeanFactroy在第一次使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化, 而ApplicationContext是在容器启动时,一次性创建并加载了所有的Bean。

 

七、Bean的作用域:

<bean>的scope属性:

singleton:默认值,在初始化IOC容器时就会创建Bean对象,且是单例的,即在整个IOC容器的生命周期内获取到的始终是同一个Bean。

prototype:在初始化IOC容器时并不会创建Bean,而是在IOC容器初始化后每次从IOC容器中获取都会创建一个新的Bean实例。

request:在web项目中,每次http请求都会创建一个新的Bean实例。

session:在web项目中,会为每一个http session会话都会创建一个新的Bean实例。

globalSession:用于分布式web开发,创建的实例绑定全局session对象。

 

八、Bean的生命周期:

Spring IOC容器可以管理bean的生命周期,并允许在bean生命周期的特定点执行特定的任务。

Spring IOC容器对bean的生命周期进行管理的过程:

1)通过构造器或工厂方法创建bean实例。

2)为bean的属性注入值。

3)调用bean的初始化方法。

4)使用bean对象。

5)当IOC容器关闭时,调用bean的销毁方法。

在<bean>中通过init-method属性和destory-method属性分别为bean指定初始化方法和销毁方法。

 

九、Spring对JDBC的支持:                             

为了使JDBC更加易于使用,Spring在JDBC API的基础上定义了一个抽象层,以此建立了一个JDBC框架。

Spring JDBC框架的核心是JDBC模板类JdbcTemplate,在JdbcTemplate类中提供了针对不同类型的JDBC操作的模板方法。每个模板方法都能完成其对应JDBC操作的整个过程,并允许覆盖JDBC操作过程的特定任务。通过这种方式,可以在尽可能保持灵活性的情况下,将JDBC操作实现的工作量降到最低。(其实JdbcTemplate类就相当于DBUtils工具中的QueryRunner类,JdbcTemplate类中的模板方法就相当于QueryRunner类中的封装方法)。

 

JdbcTemplate类的常用模板方法:

 1>更新操作(增删改):

 int | update(String sql,Obejct...args)

参数一是sql模板语句;参数二是sql模板语句的参数列表;返回值是影响行数。

 

2>批处理操作:

 int[] | batchUpdate(String sql,List<Object[]> args)

参数一是sql模板语句;参数二是往批中添加的sql模板语句的多组参数;返回值是影响行数。

 

3>查询单行:

Object | queryForObject(String sql,RowMapper<T> rowMapper,Object...agrs)

参数一是sql模板语句;参数三是sql模板语句的参数列表;参数二RowMapper对象叫行映射对象,表示如何映射查询到的结果行,其常用实现类BeanPropertyRowMapper表示将查询到的行映射到实体对象;返回值是封装了行数据的实体对象。

在参数一的sql模板语句中还可以使用列的别名来指定查询到的列映射的实体对象的属性,即即使表的列名和实体类的属性名不同,可以使用和实体类的属性名相同的列别名指定列所映射的实体类的属性。

 

Map<String,Object> | queryForMap(String sql,Object...args)

参数一是sql模板语句;参数二是sql模板语句的参数列表;返回值是Map<String,Object>表示将查询到行数据以列名映射值的键值对存储到Map集合。

 

4>查询多行:

 List<T> | query(String sql,RowMapper<T> rowMapper, Object...args)

参数一是sql模板语句;参数三是sql模板语句的参数列表;参数二RowMapper行映射对象;返回值List<T>表示将查询到的行数据保存到实体对象中再将所有实体对象保存到List集合。

 

List<Map<String,Object>> | queryForList(String sql,Object...args)

参数一是sql模板语句;参数二是sql模板语句的参数列表;返回值List<Map<String,Object>>表示将查询到的每行数据以列名映射值的键值对保存到Map集合再将所有的Map集合保存到List。

 

5>查询单行单列的值:

Object | queryForObject(String sql,Class type,Object...args)

参数一是sql模板语句;参数三是sql模板语句的参数列表;参数二是查询到的单行单列的值的类型;返回值就是查询到的单行单列的值。  

 

6>获取统计查询的单值:

Obejct | queryForObject(String sql,Class type,Object...args)

参数一是sql模板语句;参数三是sql模板语句的参数列表;参数二是统计查询到的值的类型;返回值是统计查询的值。

 

十、Spring注解:

1.基于注解的形式配置Bean:

使用基于注解的形式配置Bean,Spring IOC容器能够从classpath类路径(即src目录)下自动扫描到具有特定注解的组件(类)并对其自动实例化。

特定注解包括:

1)@Component:最基本的注解,标识一个受Spring管理的普通类。

2)@Controller:建议标识表述层的组件(控制器类)。

3)@Repository:建议标识持久层的组件(dao类)。

4)@Service:建议标识业务层的组件(业务类)。

对于扫描到的具有特定注解的组件(类),Spring IOC容器会自动创建其bean实例,且其id引用名称默认为其类名首字母小写;也可以在注解中使用参数value指定bean的id引用名称。

在组件类上定义了特定的注解之后,还需要在Bean配置文件中使用context schema约束中的<context:component-scan>标签,指定Spring IOC容器所要扫描的类路径下的指定的包;base-package属性指定了Spring IOC容器所要扫描的包的完整路径,Spring IOC容器则会扫描该包及其子包中的所有类;如果需要扫描多个包时,可以在base-package属性中使用,分隔多个包路径。

 

在<context:component-scan>标签下还有两个子标签<context:include-filter>和<context:exclude-filter>,<context:include-filter>标签用于过滤扫描所要包含的目标类,<context:exclude-filter>标签用于过滤扫描所排除掉的目标类。

<context:include-filter>和<context:exclude-filter>标签都带有type属性用于指定过滤方式,属性值annotation表示过滤具有指定注解的目标类,属性值assignable表示过滤指定类及其子类。

使用<context:include-filter>标签过滤扫描所要包含的目标类时,还需要将<context:component-scan>标签的use-default-filters属性值设置为false。

 

2.Spring注解形式装配bean的bean属性(建立bean与bean的关联关系):

<context:component-scan>标签在扫描指定的包中带有@Component、@Controller、@Repository、@Service等注解的类创建其bean实例的同时,还会自动注册bean后置处理器AutowriedAnnotationBeanPostProcessor。此后置处理器会扫描Spring IOC容器中所有的bean,当发现bean中拥有带有@Autowired、@Resource注解的bean属性时,就会对bean属性进行自动装配。

 

1)@Autowired注解                             

@Autowired注解是按照类型给bean自动装配bean属性的,即从IOC容器中获取到和bean的bean属性类型兼容的单个bean,再将其自动装配给bean的bean属性。

@Autowired注解在bean属性、构造器、带有bean属性类型的参数的方法上都可以使用。

 

默认情况下,一个bean中所有使用@Autowired注解的bean属性,在Spring IOC容器中都需要存在对应的bean用于自动装配bean的bean属性,否则会抛出异常;若使用了@Autowired注解的某一bean属性允许不被自动装配,可以设置@Autowired注解的required参数值为false,则bean对象的bean属性默认为null。

 

默认情况下,当Spring IOC容器中存在多个和bean的bean属性类型兼容的bean时,使用@Autowired注解通过类型对bean的bean属性进行自动装配时,因为不确定性是不能实现的,会抛出异常;

解决方式一,可以将Spring IOC容器中存在的多个和bean的bean属性类型兼容的bean,其中的某一个bean的id引用名称设置为和bean的bean属性名相同,那么Spring IOC容器会自动将该bean装配给bean的bean属性。

解决方式二,可以使用@Qualifier注解给bean的bean属性指定装配指定id引用名称的bean;Spring还允许给方法的参数标注@Qualifier注解,以指定方法参数装配的指定id引用名称的bean。

 

@Autowired注解也可以应用在数组类型的bean属性上,此时Spring会将Spring IOC容器中,所有和数组类型的bean属性的数组元素类型匹配的所有bean对象,自动装配给数组类型的bean属性。

@Autowired注解也可以应用在Collection集合类型的bean属性上,此时Spring也会将Spring IOC容器中,所有和Collection集合类型的bean属性的集合元素类型匹配的所有bean对象,自动装配给Collection集合类型的bean属性。

@Autowired注解也可以应用在Map集合类型的bean属性上,若Map集合的键是String类型值是bean类型,那么Spring会将Spring IOC容器中,所有和Map集合类型的bean属性的值类型匹配的所有bean对象,自动装配给Map集合类型的bean属性的值,bean的id引用名称作为Map集合的键。

 

2)@Resource注解:                             

@Resource注解是按照id引用名称给bean自动装配bean属性的,其name参数用于指定id引用名称。即从IOC容器中获取到@Resource注解的name参数指定的id引用名称的bean,再将其自动装配给bean的bean属性。

@Resource注解在bean属性、带有bean属性类型参数的方法上可以使用。

 

如果没有指定@Resource注解的name参数,当注解是作用在bean属性上面的,默认将IOC容器中和bean的bean属性名相同的bean对象,自动装配给bean属性;

如果没有指定@Resource注解的name参数,当注解是作用在带有bean属性类型参数的方法上的,默认将IOC容器中和方法参数名相同的id引用名称的bean对象,自动装配给bean属性。

 

十一、Spring声明式事务

Spring框架在各个数据库技术的事务操作API之上定义了一个抽象层,就是Spring的事务管理。而开发人员不必了解其底层的事务操作API,就可以使用Spring的事务管理完成各个数据库技术的事务操作。 

而其原理是使用了AOP,把事务操作的横切关注点从业务方法中抽取出来定义在切面中,获取连接对象并开启事务定义为切面的前置通知,提交事务定义为切面的返回通知,回滚事务定义为切面的异常通知,关闭连接对象定义为切面的后置通知,这种方式叫做Spring的声明式事务。

Spring框架在各个数据库技术的事务操作API之上定义了一个抽象层,而该抽象层的核心就是事务管理器org.springframework.tansaction.PlatformTransactionManager接口,在事务管理器中封装了适用于不同数据库技术的一套完整的事务操作机制,事务管理器的本质其实就是封装了事务操作的切面。对于不同的数据库技术,事务管理器提供了不同的实现,完成不同数据库技术的事务操作都必须创建其对应的事务管理器

适用于JDBC的事务操作:org.springframework.jdbc.datasource.DataSourceTransactionManager:

适用于Hibernate框架的事务操作:org.springframework.orm.hibernate3.HibernateTransactionManager

适用于JTA的事务操作:org.springframework.transaction.jta.JtaTransactionManager

 

基于注解的实现

 

1>在IOC容器中配置事务管理器(事务管理器即是封装了事务操作的切面)。

 

2>在bean配置文件中使用tx schema约束中的<tx:annotation-driven>标签启用事务管理的注解支持,并指定事务管理器。

 

3>使用@Transaction注解标注事务方法(被@Transaction注解标注的事务方法即是切入点)。

 

@Transaction注解可以用在public的方法上,也可以使用在类上,如果使用在public的方法上表示当前方法支持事务操作,如果使用在类上表示当前类中的所有的public方法支持事务操作。

 

 

基于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"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-4.0.xsd

    http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-4.0.xsd

    http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-4.0.xsd">

 

   <!-- 

     引入外部属性文件:

   -->

   <context:property-placeholder location="classpath:jdbc.properties"/>

  

   <!-- 

     配置数据库连接池对象,并给其注入jdbc四大参数值

   -->

   <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">

     <property name="driverClass" value="${driverClass}"></property>

     <property name="jdbcUrl" value="${url}"></property>

     <property name="user" value="${uname}"></property>

     <property name="password" value="${upass}"></property>

   </bean>

  

   <!-- 

     配置JDBC模板类的bean实例,并将配置好的数据库连接池对象ds注入给其持有的DataSource数据库

     连接池对象属性dataSource

   -->

   <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate">

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

   </bean>

  

   <!-- 

    配置AccountDao类的bean实例,并将配置好的JDBC模板类对象template注入给其持有的

    JDBC模板类对象属性template

   -->

   <bean id="ad" class="com.mmy.dao_xml.AccountDao">

     <property name="template" ref="template"></property>

   </bean>

  

   <!-- 

     配置AccountService类的bean实例,并将配置好的AccountDao类对象ad注入给其持有的

     AccountDao类的对象属性ad

   -->

   <bean id="as" class="com.mmy.service_xml.AccountService">

     <property name="ad" ref="ad"></property>

   </bean>

  

   <!--

    配置AccountSuperService类的bean实例,并将配置好的AccountService类的对象

    as注入给其持有的AccountService类的对象属性as

   -->

   <bean id="ass" class="com.mmy.service_xml.AccountSuperService">

     <property name="as" ref="as"></property>

   </bean>

  

   <!-- 

     基于xml配置实现声明式事务:

   -->

   <!-- 

     1.配置事务管理器(事务管理器即是封装了事务操作的切面)。

       并将配置好的数据库连接池对象ds注入给其持有的DataSource数据库连接池对象属性dataSource

   -->

   <bean id="manager"

      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

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

   </bean>

  

   <!-- 

     2.设置事务管理器的事务属性(即是切面的通知)。

       id属性:指定通知名称为transactionAdvice

       transaction-manager属性:指定事务管理器为manager

   -->

   <tx:advice id="transactionAdvice" transaction-manager="manager">

      <tx:attributes>

        <!-- 

         1>事务方法transMoney()的事务传播方式是REQUIRES_NEW,即使用自己的事务操作,调用

           transMoney()方法的superTransMoney()方法的事务挂起;

         2>事务方法transMoney()的事务隔离级别是READ_COMMITTED;

         3>事务方法transMoney()的事务超时时间为5秒,即5秒之后事务未执行结束则自动回滚事务。

        -->

        <tx:method name="transMoney" propagation="REQUIRES_NEW"

                                     isolation="READ_COMMITTED"

                                     timeout="5"/>

        <!--其它所有事务方法的事务属性都使用默认值-->

        <tx:method name="*"/>                         

      </tx:attributes>

   </tx:advice>

  

   <!-- 

     3.配置事务切入点,并将切入点和通知关联起来。

   -->

   <aop:config>

      <!--

       1>expression属性指定切入点表达式execution(* com.mmy.service_xml.*.*(..))

         表示com.mmy.service_xml包下的所有类的任意修饰符任意返回值类型任意参数的所有方法都进

行事务操作(重点是AccountService的transMoney()方法和AccountSuperService()方法

         都进行事务操作)

       2>id属性指定切入点名称为transCutPoint

      -->

     <aop:pointcut expression="execution(* com.mmy.service_xml.*.*(..))"

                                                       id="transCutPoint"/>

     <!--将名称为transactionAdvice的通知和名称为transCutPoint的切入点关联起来-->                                                

     <aop:advisor advice-ref="transactionAdvice" pointcut-ref="transCutPoint"/>                                                

   </aop:config>

 

</beans>


其它:

 

1>事务的传播方式:                                 

一个事务方法被另一个事务方法调用时,必须指定事务的传播方式。即被调用的事务方法是使用调用者事务方法的事务操作,还是使用自己的事务操作。

 

 事务的传播方式由@Transaction注解的参数propagation指定,常见值有:

 

 1)REQUIRED:如果调用者方法有事务操作,那么被调用方法就使用调用者方法的事务操作;如果调用者方法没有事务操作,那么被调用方法就是使用自己的事务操作。

 2)REQUIRES_NEW:被调用方法必须使用自己的事务操作,如果调用者方法也有事务操作则被挂起。

 

2>事务隔离级别:

当多个事务在同一个数据集上进行并发操作时,可能造成事务的并发读问题。常见的事务并发读问题有:

1)脏读:对于两个事务T1和T2,T1事务读取了T2事务更新但是还未提交的字段,之后若T2回滚,T1读取的数据就是临时且无效的。

2)不可重复读:对于两个事务T1和T2,T1读取了一个字段,然后T2更新了该字段,之后T1再次读取该字段,值就不同了。

3)幻读:对于两个事务T1和T2,T1从表中读取了一个字段,然后T2向该表中插入了新的行,之后T1再次读取该表,就多出了几行。

可以通过@Transaction注解的isolation参数设置事务的隔离级别,来对多个并发事务进行隔离,解决事务的并发读问题。常见值有:

1)SERIALIZABLE:确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其它事物对该表做插入、更新和删除操作。所有的并发读问题都可以解决,但是性能十分低下。

2)READ-COMMIT:只允许事务读取已经被其它事务提交的变更数据。可以避免脏读,但不能避免不可重复读和幻读问题。一般都使用默认值READ-COMMIT。

3)REPEATABLE-READ:确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其它事物对这个字段进行更新。可以避免脏读和不可重复读,但是不能避免幻读。

4)READ-UNCOMMIT:允许事务读取未被其它事务提交的变更数据。脏读、不可重复读和幻读问题都可能出现,但是效率极高。

事务的隔离级别要得到底层数据库的支持,而不是应用程序或者框架的支持。Oracle支持两种事务隔离级别,SERIALIZABLE和READ-COMMIT;MySQL支持所有四种隔离级别。

 

3>设置事务的回滚:                                  

默认情况下遇到所有的运行时异常时都会导致事务的回滚,但还可以通过@Transaction注解的rollbackFor参数设置遇到哪些具体的运行时异常时回滚事务,通过noRollbackFor参数设置遇到哪些具体的运行时异常时不回滚事务。rollbackFor参数和noRollbackFor参数的值都是运行时异常类的类型(Class)。但是一般不去设置事务的回滚,默认遇到所有运行时异常都回滚事务。

 

4>只读事务:                                          

如果一个事务只是读取数据而不对数据做更新,那么可以将@Transaction注解的readOnly参数值设置为true,表示将事务设置为只读事务,这样可以帮助数据库优化事务,提高执行效率。readOnly参数的默认值为false。

 

5>设置事务超时:                                      

由于事务的操作可以在行或表上获得锁,因此如果一个事务的执行时间过长会很占用资源,对程序整体性能产生影响。那么可以通过@Transaction注解的timeout参数设置事务的超时时间,当事务执行时间超出timeout参数设置的时间时会强制回滚事务。timeout参数值单位为秒。

 

 

 

 

 

 

 

 

 

posted @ 2020-07-09 11:23  一个农民  阅读(244)  评论(0)    收藏  举报