Spring


Spring基础

Spring简介

1)Spring是一个开源框架,为简化企业级应用开发而生,Spring可以使用简单的JavaBean实现以前只有EJB才能实现的功能,Spring是一个IOC和AOP容器框架。

2)Spring容器是Spring的核心,一切Spring bean都存储在Spring容器内,并由其通过IOC技术管理。Spring容器也就是一个bean工厂(BeanFactory)。应用中bean的实例化,获取,销毁等都是由这个bean工厂管理的。

3)org.springframework.context.ApplicationContext接口用于完成容器的配置,初始化,管理bean。一个Spring容器就是某个实现了ApplicationContext接口的类的实例。也就是说,从代码层面,Spring容器其实就是一个ApplicationContext。

4)在普通的JAVA工程中,我们可以通过代码显式new一个ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext来初始化一个Spring容器。在Web工程中,我们一般是通过配置web.xml的方式来初始化Spring容器。

 

Spring特征

1)轻量:从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布,并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的,Spring应用中的对象不依赖于Spring的特定类。

2)IOC(控制反转):Spring通过一种称作控制反转的技术促进了低耦合。当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IOC与JNDI相反,即不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

3)AOP(面向切面):Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务,例如审计和事务管理进行内聚性的开发。应用对象只实现业务逻辑仅此而已,它们并不负责其它的系统级关注点,例如日志或事务支持。

4)容器:Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,基于一个可配置原型,你可以配置你的每个bean如何被创建,你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例,以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

5)框架:Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里进行配置。Spring也提供了很多基础功能,如事务管理、持久化框架集成等等,开发者只需要进行应用逻辑的开发。

6)MVC:Spring的作用是整合,但不仅仅限于整合,Spring框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,前端控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping将请求映射到对应的处理器Controller,在Spring当中如果写一些处理器组件,一般实现Controller接口,在Controller中就可以调用一些Service或DAO来进行数据操作,ModelAndView用于存放从DAO中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring框架中还提供一个视图组件ViewResolver,该组件根据Controller返回的标示,找到对应的视图,将响应response返回给用户。

所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

 

Spring结构

核心容器:包括Core、Beans、Context、EL模块

1)Core模块:封装了框架依赖的最底层部分,包括资源访问、类型转换及一些常用工具类。

2)Beans模块:提供了框架的基础部分,包括控制反转和依赖注入。其中Bean Factory是容器核心,本质是“工厂设计模式”的实现,而且无需编程实现“单例设计模式”,单例完全由容器控制,而且提倡面向接口编程,而非面向实现编程;所有应用程序对象及对象间关系由框架管理,从而真正把你从程序逻辑中把维护对象之间的依赖关系提取出来,所有这些依赖关系都由BeanFactory来维护。

3)Context模块:以Core和Beans为基础,集成Beans模块功能并添加资源绑定、数据验证、国际化、Java EE支持、容器生命周期、事件传播等;核心接口是ApplicationContext。

4)EL模块:提供强大的表达式语言支持,支持访问和修改属性值,方法调用,支持访问及修改数组、容器和索引器,命名变量,支持算数和逻辑运算,支持从Spring容器获取Bean,它也支持列表投影、选择和一般的列表聚合等。

 

AOP、Aspects模块

1)AOP模块: Spring AOP模块提供了符合AOP Alliance规范的面向切面的编程实现,提供比如日志记录、权限控制、性能统计等通用功能和业务逻辑分离的技术,并且能动态的把这些功能添加到需要的代码中。这样各专其职,降低业务逻辑和通用功能的耦合。

2)Aspects模块:提供了对AspectJ的集成,AspectJ提供了比Spring ASP更强大的功能。

 

数据访问/集成模块:该模块包括了JDBC、ORM、OXM、JMS和事务管理。

1)事务模块:该模块用于Spring管理事务,只要是Spring管理对象都能得到Spring管理事务的好处,无需在代码中进行事务控制了,而且支持编程和声明式的事务管理。

2)JDBC模块:提供了一个JBDC的样例模板,使用这些模板能消除传统冗长的JDBC编码还有必须的事务控制,而且能享受到Spring管理事务的好处。

3)ORM模块:提供与流行的“对象-关系”映射框架的无缝集成,包括Hibernate、JPA、MyBatis等。而且可以使用Spring事务管理,无需额外控制事务。

4)OXM模块:提供了一个对Object/XML映射实现,将java对象映射成XML数据,或者将XML数据映射成java对象,Object/XML映射实现包括JAXB、Castor、XMLBeans和XStream。

5)JMS模块:用于JMS(Java Messaging Service),提供一套“消息生产者、消息消费者”模板用于更加简单的使用JMS,JMS用于用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

 

Web/Remoting模块:Web/Remoting模块包含了Web、Web-Servlet、Web-Struts、Web-Porlet模块。

1)Web模块:提供了基础的web功能。例如多文件上传、集成IOC容器、远程过程访问(RMI、Hessian、Burlap)以及Web Service支持,并提供一个RestTemplate类来提供方便的Restful services访问。

2)Web-Servlet模块:提供了一个Spring MVC Web框架实现。Spring MVC框架提供了基于注解的请求资源注入、更简单的数据绑定、数据验证等及一套非常易用的JSP标签,完全无缝与Spring其他技术协作。

3)Web-Struts模块:提供了与Struts无缝集成,Struts1.x和Struts2.x都支持。

 

Test 模块:Spring支持Junit和TestNG测试框架,而且还额外提供了一些基于Spring的测试功能,比如在测试Web框架时,模拟Http请求的功能。

 

Spring中设计模式

1)代理模式:Spring中有两种代理方式,若目标对象实现了若干接口,Spring使用jdk的java.lang.reflect.Proxy类代理。若目标没有实现任何接口,Spring使用CGLIB库生成目标类的子类。

2)单例模式:在Spring的配置文件中设置的bean默认为单例模式。

3)模板方法模式:用来解决代码重复的问题,比如:RestTemplate、JmsTemplate、JpaTemplate。

4)工厂模式:在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来指向新创建的对象,Spring中使用beanFactory来创建对象的实例。

5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现ApplicationListener。

 

Spring核心

IOC详细说明

1)IOC(控制反转),传统的java开发模式中,当需要一个对象时,我们会自己使用new或者getInstance等直接或者间接调用构造方法创建一个对象。而在Spring开发模式中,Spring容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用Spring提供的对象就可以了,这就是控制反转的思想。

2)IOC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试。有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入、组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

3)其实IOC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IOC/DI思想中,应用程序就变成被动的了,被动的等待IOC容器来创建并注入它所需要的资源了。

4)IOC很好的体现了面向对象设计法则之一,好莱坞法则:“别找我们,我们找你”,即由IOC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

5)IOC(Inversion of Control,控制反转)。这是Spring的核心,贯穿始终。所谓IOC,对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象间的关系。Spring所倡导的开发方式就是如此,所有的类都会在Spring容器中登记,告诉Spring你是个什么东西,你需要什么东西,然后Spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由Spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被Spring控制,所以这叫控制反转。

 

DI详细说明

1)DI(依赖注入),Spring使用javaBean对象的set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入。

2)DI(Dependency Injection):组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件复用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

3)平常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,Spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。依赖注入的另一种说法是“控制反转”,通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员,而控制反转是指new实例工作不由我们程序员来做而是交给Spring容器来做。

4)IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI来实现的。依赖注入的方式有:Set注入、构造器注入、静态工厂的方法注入、实例工厂的方法注入等。

 

AOP详细说明

1)AOP(面向切面编程),在OOP(面向对象编程)思想中,我们将事物纵向抽成一个个的对象,而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程。AOP底层是动态代理,如果是接口采用JDK动态代理,如果是类采用CGLIB方式实现动态代理。

2)AOP编程思想:横向重复代码,纵向抽取,就是说多个地方重复的代码可以抽取出来公用(过滤器等可以体现)。

3)AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。AOP实现的关键就在于AOP框架自动创建的AOP代理,AOP代理则可分为静态代理和动态代理两大类,其中静态代理是指使用AOP 框架提供的命令进行编译,从而在编译阶段就可生成AOP代理类,因此也称为编译时增强;而动态代理则在运行时借助于JDK动态代理、CGLIB等在内存中“临时”生成AOP动态代理类,因此也被称为运行时增强。

4)Spring的AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可由IOC容器的依赖注入提供。纵观AOP编程,其中需要程序员参与的只有3个部分:

一、定义普通业务组件。

二、定义切入点,一个切入点可能横切多个业务组件。

三、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作。

上面3个部分的第一个部分是最平常不过的事情,无须额外说明。那么进行AOP编程的关键就是定义切入点和定义增强处理。一旦定义了合适的切入点和增强处理,AOP框架将会自动生成AOP代理。

5)核心关注点(业务)是应用中一个模块的行为,一个核心关注点可能会被定义成一个我们想实现的一个功能。横切关注点(复用的,与业务无关)是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能,因此这些都属于横切关注点。

 

BeanFactory、ApplicationContext

BeanFactory可以理解为就是个HashMap,Key是BeanName,Value是Bean实例。通常只提供注册、获取这两个功能,称之为“低级容器”。

ApplicationContext可以称之为“高级容器”,他继承了多个接口,因此具备了更多的功能,例如资源的获取,支持多种消息(如JSP tag的支持),比BeanFactory多了工具级别的支持等等。他的名字已经不是BeanFactory之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh方法,此方法是所有阅读Spring源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的bean。

 

常用实现类

1)BeanFactory实现类

Bean工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。常用的BeanFactory实现有DefaultListableBeanFactory 、 XmlBeanFactory 、ApplicationContext等。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML文件读取配置元数据并用它去创建一个完全配置的系统或应用。

2)ApplicationContext实现类

FileSystemXmlApplicationContext:此容器从一个XML文件中加载beans的定义,XML Bean配置文件的全路径名必须提供给它的构造函数。

ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。

WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。

 

区别

1)功能区别

BeanFactory是Spring里面最底层的接口,包含了各种Bean的定义,读取Bean配置文档,管理Bean的加载、实例化,控制Bean的生命周期,维护Bean之间的依赖关系。

ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:继承MessageSource,因此支持国际化、统一的资源文件访问方式、提供在监听器中注册bean的事件、同时加载多个配置文件、载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

2)加载方式

BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean,确保当你需要的时候,你就不用等待,因为它们已经创建好了。相对于基本的BeanFactory,ApplicationContext唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

3)创建方式与注册方式

BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

 

SpringBean

SpringBean详细说明

1)SpringBeans是那些形成Spring应用的主干的java对象。它们被IOC容器初始化,装配,和管理。这 些beans通过容器中配置的元数据创建。比如,以XML文件中的形式定义。

2)一个SpringBean的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖。

3)当定义一个bean在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope属性必须设为singleton。

4)Spring框架支持以下五种bean的作用域:

singleton:bean在每个IOC容器中只有一个实例,默认的Spring bean的作用域是Singleton。

prototype:一个bean的定义可以有多个实例。

request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。

session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于 web的Spring ApplicationContext情形下有效。

 

SpringBean生命周期

1)bean定义:在配置文件里面用来进行定义。

2)bean初始化:初始化有两种方式,1个是在配置文件中通过指定init-method属性来完成,另一个是实现org.springframwork.beans.factory.InitializingBean接口。

3)bean调用:有三种方式可以得到bean实例,并进行调用,分别是:使用构造器实例化、使用静态工厂方法实例化、使用实例化工厂方法实例化。

4)bean销毁:销毁有两种方式,1种是使用配置文件指定的destroy-method属性,另一种是实现org.springframwork.bean.factory.DisposeableBean接口。

 

SpringBean线程安全

Spring框架中的单例bean不是线程安全的。Spring中的bean默认是单例模式,Spring框架并没有对单例 bean进行多线程的封装处理。实际上大部分时候spring bean是无状态的(比如dao类),所以某种程度上来说bean也是安全的,但如果bean有状态的话(比如view model对象),那就要开发者自己去保证线程安全了,最简单的就是改变bean的作用域,把“singleton”变更为“prototype”,这样请求bean相当于 new Bean()了,所以就可以保证线程安全了。有状态就是有数据存储功能,无状态就是不会保存数据。

Spring如何处理线程并发问题:在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

 

Spring事务

事务是并发控制的单位,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。

 

事务四大特征(ACID)

原子性:表示事务内操作不可分割,要么都成功、要么都是失败。

一致性:要么都成功,要么都是失败,后面的失败了要对前面的操作进行回滚。

隔离性:一个事务开始后,不能被其他事务干扰。

持久性/持续性:表示事务开始了,就不能终止。

 

编程式事务

在代码中显式挪用beginTransaction()、commit()、rollback()等事务治理相关的方法,这就是编程式事务管理。Spring对事务的编程式管理有基于底层API的编程式管理和基于TransactionTemplate的编程式事务管理两种方式。

1)基于底层API的编程式管理:凭证PlatformTransactionManager 、TransactionDefinition和TransactionStatus三个焦点接口,来实现编程式事务管理。

Public class BankServiceImpl implements BancService{ 
    Private BanckDao bankDao; 
    private TransactionDefinition txDefinition; 
    private PlatformTransactionManager txManager; 
    ...... 
    public boolean transfer(Long fromId, Long toId, double amount) { 
        //获取事务
        TransactionStatus txStatus = txManager.getTransaction(txDefinition); 
        boolean result = false; 
        try { 
            result = bankDao.transfer(fromId, toId, amount); 
            //提交事务
            txManager.commit(txStatus); 
        } catch (Exception e) { 
            result = false; 
            //回滚事务
            txManager.rollback(txStatus); 
            System.out.println("Transfer Error!"); 
        } 
        return result; 
    }
}

2)基于TransactionTemplate的编程式事务管理:为了不损坏代码原有的条理性,避免出现每一个方法中都包括相同的启动事务、提交、回滚事物样板代码的现象,Spring提供了TransactionTemplate模板来实现编程式事务管理。

public class BankServiceImpl implements BankService { 
    private BankDao bankDao; 
    private TransactionTemplate transactionTemplate; 
    ...... 
    //模板过程(1.打开事务 2.调用doInTransaction方法 3.提交事务)
    public boolean transfer(final Long fromId, final Long toId, final double amount) { 
        return (Boolean) transactionTemplate.execute(new TransactionCallback(){ 
            //实现对事务的管理
            public Object doInTransaction(TransactionStatus status) { 
                Object result; 
                try { 
                    result = bankDao.transfer(fromId, toId, amount); 
                } catch (Exception e) { 
                    status.setRollbackOnly(); 
                    result = false; 
                    System.out.println("Transfer Error!"); 
                } 
                return result; 
            } 
        }); 
    } 
} 

声明式事务

用在Spring配置文件中声明式的处理事务来代替代码式的处理事务。这样的好处是,事务管理不侵入开发的组件,具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可,这样维护起来极其方便。

1)基于TransactionInterceptor的声明式事务管理:两个次要的属性,transactionManager,用来 指定一个事务治理器,并将具体事务相关的操作委托给它;另外一个一个是Properties类型的transactionAttributes属性,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符, 而值就是表现呼应方法的所运用的事务属性。

class="org.springframework.transaction.interceptor.TransactionInterceptor"> 
     
     
         
            PROPAGATION_REQUIRED 
         
     
 
class="footmark.spring.core.tx.declare.origin.BankServiceImpl"> 
     
 
class="org.springframework.aop.framework.ProxyFactoryBean"> 
     
     
         
             
         
     
 

2)基于TransactionProxyFactoryBean的声明式事务管理:设置配置文件与先前比照简化了许多。我们把这类设置配置文件格式称为Spring经典的声明式事务治理。

class="footmark.spring.core.tx.declare.classic.BankServiceImpl"> 
     
 
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
     
     
     
         
            PROPAGATION_REQUIRED 
         
     
 

3)基于tx命名空间的声明式事务治理:在前两种方法的基础上,Spring 2.x引入了tx命名空间,带给开发人员设置配备声明式事务的全新体验。



    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
    



    
    
    
    
    

4)基于@Transactional的声明式事务管理:Spring 2.x还引入了基于Annotation的体式格式,@Transactional可以浸染于接口、接口方法、类和类方法上。作用于类上时,该类的一切public方法将都具有该类型的事务属性。

@Transactional(propagation = Propagation.REQUIRED) 
public boolean transfer(Long fromId, Long toId, double amount) { 
    return bankDao.transfer(fromId, toId, amount); 
}

事务传播行为

Spring事务的传播行为说的是,当多个事务同时存在的时候,Spring如何处理这些事务的行为。

1)PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

2)PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

3)PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

4)PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

5)PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6)PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

7)PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

 

事务隔离级别

脏读:表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录A,此时该事务还未提交,然后另一个事务尝试读取到了记录A。

不可重复读:是指在一个事务内,多次读同一数据。

幻读:指同一个事务内多次查询返回的结果集不一样。比如同一个事务A第一次查询时候有n条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

Spring有五大隔离级别,默认值为ISOLATION_DEFAULT,其他四个隔离级别和数据库的隔离级别一致:

1)ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么就用什么。

2)ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)。

3)ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL Server的默认级别。

4)ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL的默认级别。

5)ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

 

Spring通知

基础术语

1)AOP相关概念

连接点(Joinpoint):目标对象中,所有可以增强的方法

切入点(Pointcut):目标对象,已经增强的方法

通知/增强(Advice):增强的代码

目标对象(Target):被代理对象

织入(Weaving):将通知应用到切入点的过程

代理(Proxy):将通知织入到目标对象之后,形成代理对象

切面(aspect):切入点+通知

2)通知类型

前置通知:目标方法运行之前

后置通知(如果出现异常不会调用):之后

环绕通知:之前之后

异常拦截通知:出现异常调用

后置通知(无论是否出现异常都会调用):之后

 

通知方式

1)通过配置文件设置通知,需要定义一个通知类MyAdviceAspect



class="com.item.spring.service.UserServiceImpl">

class="com.item.spring.aspect.MyAdviceAspect">



    
    
    
    
    
    
?
    
    
?
    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
    

2)利用注解实现通知

@Aspect
public class MyAdviceAnno {
    /**
     * 方便管理切入点
     */
    @Pointcut("execution(* com.item.spring.service.*ServiceImpl.*(..))")
    public void pc() {
    }
?
    /**
     * 配置通知,并指定织入到哪去
     * 前置通知:目标方法运行之前
     */
    @Before("com.item.spring.aspect.MyAdviceAnno.pc()")
    public void before() {
        System.out.println("前置通知");
    }
?
    /**
     * 后置通知(如果出现异常不会调用):之后
     */
    @AfterReturning("execution(* com.item.spring.service.*ServiceImpl.*(..))")
    public void afterReturning() {
        System.out.println("后置通知(如果出现异常不会调用)");
    }
?
    /**
     * 环绕通知:之前之后
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.item.spring.service.*ServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知之前部分");
        //调用目标方法
        Object proceed = pjp.proceed();
        System.out.println("环绕通知之后部分");
        return proceed;
    }
?
    /**
     * 异常拦截通知:出现异常调用
     */
    @AfterThrowing("execution(* com.item.spring.service.*ServiceImpl.*(..))")
    public void afterException() {
        System.out.println("出现异常调用");
    }
?
    /**
     * 后置通知(无论是否出现异常都会调用):之后
     */
    @After("execution(* com.item.spring.service.*ServiceImpl.*(..))")
    public void after() {
        System.out.println("后置通知(无论是否出现异常都会调用)");
    }
}

Spring注解

注解说明
@Component 这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller 这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IOC 容器中。
@Service 此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
@Repository 这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IOC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。
@Required 应用于 bean 属性 setter 方法。此注解仅指示必须在配置时使用 bean 定义中的显式属性值或使用自动装配填充受影响的 bean 属性。如果尚未填充受影响的 bean 属性,则容器将抛出 BeanInitializationException。
@Autowired 可以更准确地控制应该在何处以及如何进行自动装配。此注解用于在 setter 方法,构造函数,具有任意名称或多个参数的属性或方法上进行自动装配bean。默认情况下,它是类型驱动的注入。
@Qualififier 当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualififier 注解和 @Autowired 通过指定应该装配哪个确切的 bean来消除歧义。

 

Autowired和Resource

@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

共同点:两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

不同点:

1)@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired,只按照byType注入。

// 下面两种@Autowired只要使用一种即可 
public class TestServiceImpl {
    // 用于字段上
    @Autowired private UserDao userDao; 
    
    // 用于属性的方法上 
    @Autowired 
    public void setUserDao(UserDao userDao) { 
        this.userDao = userDao; 
    } 
}

@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualififier注解一起使用。如下:

public class TestServiceImpl { 
    @Autowired 
    @Qualifier("userDao") 
    private UserDao userDao; 
}

2)@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

// 下面两种@Resource只要使用一种即可
public class TestServiceImpl { 
    // 用于字段上
    @Resource(name="userDao") 
    private UserDao userDao; 
    
    // 用于属性的setter方法上
    @Resource(name="userDao") 
    public void setUserDao(UserDao userDao) {  
        this.userDao = userDao; 
    } 
}

注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。

 

@Resource装配顺序

1)如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。

2)如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

3)如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。

4)如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。