1 优点

  • Spring是一个开源的免费的框架(容器)
  • Spring是一个轻量级的、非入侵式的框架
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持
  • 总结就是:Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

    2 IOC

    2.1 IOC本质

  • 控制反转IOC(Inversion of Control)是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后对象的创建转移给第三方,个人认为所谓控制反转就是获得依赖对象的方式反转了

  • 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
  • 反转:程序本身不创建对象,而变成被动的接收对象
  • 依赖注入:
    • Dependency Injection,就是让spring框架给Bean对象的属性进行赋值。它是spring框架核心ioc的具体体现。
    • 我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。ioc解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用spring后就让spring来维护了。
    • 简单的说,依赖注入(DI)就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
  • 使用构造函数方式注入
    • 就是使用类中的构造函数,给成员变量赋值。
    • 赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。
    • 涉及的标签:constructor-arg
      • index:指定参数在构造函数参数列表的索引位置
      • type:指定参数在构造函数中的数据类型
      • name:指定参数在构造函数中的名称,用这个找给谁赋值
      • ——-上面三个都是找给谁赋值,下面两个指的是赋什么值——
      • value:它能赋的值是基本数据类型和String类型
      • ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
  • 使用set方式进行注入:就是在类中提供注入成员的set方法,(实际开发中用此方法比较多)
  • IOC是一种编程思想,由主动的编程变成被动的接收,所谓的IOC就是对象由Spring来创建,管理,装配
  • 采用XML方式配置Bean的时候,bean的定义信息是实现和分离的,而采用注解的方式可以把两者合为一体,bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
  • 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的IOC容器,其实现方法是依赖注入。

    2.2 XML的IOC细节

  • BeanFactory和ApplicationContext的区别

    • BeanFactory是Spring中IOC容器的顶层接口,而ApplicationContext是他的一个子接口,所以 ApplicationContext是具备BeanFactory提供的全部功能
    • 通常情况下,我们称BeanFactory是Spring的IOC基础容器。而ApplicationContext是容器的高级接口,它比BeanFactory多了很多重要的功能。例如,父子容器的概念,AOP的支持,消息发布机制,事件处理机制,国际化和资源访问等。
    • 区别在于创建对象的时间不一样:
      • ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
      • BeanFactory:使用时才创建对象。
  • ApplicationContext接口实现类

    • ClassPathXmlApplicationContext 它是从类的根路径下加载配置文件,推荐使用这种
    • FileSystemXmlApplicationContext 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
    • AnnotationConfigApplicationContext 当我们使用注解配置容器对象时,需要此类来创建spring容器。它用来读取注解

      2.3 Bean标签的对象获取getBean方法

      1. //共三种
      2. applicationContext.getBean("accountService") ;
      3. applicationContext.getBean("accountService",AccountService.class) ;
      4. applicationContext.getBean(AccountService.class) ;

      2.4 Bean标签的属性和作用

      作用
  • 用于配置对象让spring来创建的

  • 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功

属性

  • id:给对象在容器中提供一个唯一标识。用于获取对象
  • class:指定类的全限定名。用于反射创建对象。默认情况下调用无参构造函数
  • scope:指定对象的作用范围
    • singleton:默认值,单例的
      • 一个应用只有一个对象的实例。它的作用范围就是这个引用
      • 生命周期
        • 对象出生:当使用对象时,创建新的对象实例
        • 对象活着:只要对象在使用中,就一直活着
        • 对象死亡:当应用卸载,销毁容器时,对象就被销毁了
        • 总结:单例模式的bean独享声明周期与容器相同
    • prototype:多例的
      • 每次访问对象时,都会重新创建对象实例
      • 生命周期
        • 对象出生:当使用对象时,创建新的对象实例
        • 对象活着:只要对象在使用中,就一直活着
        • 对象死亡:当对象长时间不用时,被java的垃圾回收器回收
    • request:WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中。
    • session:WEB域中,spring创建一个Bean对象,将对象存入到Session域中。
  • init-method:指定类汇总的初始方法和名称
  • destory-method:指定类中销毁方法名称

    2.5 实例化Bean的三种方式

  • 使用默认无参构造函数来创建类对象,如果bean中没有无参构造函数,就会创建失败(常用)

    1. <bean id="accountDao2" class="com.bai.dao.impl.AccountDaoImpl2"/>
  • Spring管理静态工厂,创建业务层实现类对象

  • Spring管理实例工厂,使用实例工厂的方法创建对象

    3 spring注解开发

    Bean标签和注解的对应

  • bean标签对应的注解@Component

    • 注解属性value:bean标签id属性
    • 不指定value属性,默认就是类名,首字母小写
    • 该注解衍生出了三个注解,@Controller,@Service,@Repository,用法和Component一致,为了更加清晰的体现层的概念
  • bean标签属性scope对应属性@Scope,注解属性value:singleton,prototype
  • bean标签属性init-method对应注解@PostConstruct
  • Bean标签属性destroy-method对应注解@PreDestory

依赖注入注解

  • @Autowried注解 (Spring框架提供):按照类型注入,如果无法确定唯一类型(接口有不同的实现类),需要配合@Qualifier的使用。
  • @Qualifier(“id”)注解(Spring框架提供):按照ID注入
  • @Resources注解(JDK提供):注解属性name:配置类的ID

纯注解开发

  • @Configuration标识当前类是Spring的一个配置类
  • @ComponentScan替代xml中的
  • @Import引入其他配置类,被引入的配置类可以不加@Configuration注解
  • @ProperySource:引入外部properties文件,注意加classpath:
  • @Value 对成员变量赋值
  • @Bean将一个方法的返回值对象加入到Spring的容器当中管理
  • @Qualifier可以使用在方法参数上,表明对应的形参引入注入的对象类型

    4 spring对Junit的支持

  • junit运行的时候底层使用了Runner对象,有一个默认使用的Runner对象。

  • spring对junit的支持是自己实现了一个RUnner的对象(按照junit runner的要求实现)
  • Spring对junit的支持的体现
    • 配置完成后不需要手动启动spring
    • 在junit测试类汇总使用@AutoWired等方式注入对象,直接对其进行调用测试
  • 使用步骤:引入spring-test.jar 和 配置测试类

    5 (非spring)AOP动态代理技术

  • 动态代理作用:在不修改源码的基础上,对已有方法增强

  • 动态代理特征:字节码是随用随创建,随用随加载
  • 动态代理分类:动态代理根绝创建代理对象的类型分为两类

    • 第一类:基于接口的动态代理,即创建的代理对象和被代理对象实现了相同的接口
      • 提供者:JDK官方Proxy
      • 使用要求:被代理类最少实现一个接口
      • 创建代理对象的类:java.lang.reflect.Proxy
      • 创建代理对象的方法:public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHander h)
      • ClassLoader loader:类加载器,用于加载代理对象的字节码,写和被代理对象使用相同的类加载器
      • Class<?>[] interfaces:字节码数组,用于让代理对象和被代理对象具有相同的行为(方法);要根据被代理对象区别对象,如果被代理对象是一个普通类,那么写的就是它实现的接口数组,通常就是被代理类字节码对象调用getInterfaces()方法。如果被代理对象本身就是一个接口,那么就直接创建一个字节码数组,把接口的字节码传入。通常就是new Class[]{被代理接口的字节码}
      • Invocationhandler h :一个接口,用于给被代理对象方法提供增强代码的;编写invocationHander的实现类,重写invoke方法。在方法中提供要增强的代码。
    • 第二类:基于子类的动态代理,即创建的代理对象是被代理对象的子类
      • 提供者:第三方开源项目CGLIB(code generation libary)
      • 使用要求:被代理类不能是最终类
      • 创建代理对象的类:net.sf.cglib.proxy.Enhancer
      • 创建代理对象的方法:public static Object create(Class type,Callback callback)
      • Class type : 字节码,用于创建被代理的子类代理对象,同时用于得到加载代理对象的类加载器。写被代理对象的字节码。
      • Callback callback :一个接口,用于给被代理对象方法提供增强代码的。打开源码发现,此接口中没有任何方法。因为Callback接口是用于定义规范的标记接口,在实际开发中,我们一般使用它的子接口MethodInterceptor 。编写MethodInterceptor的实现类,重写intercept方法,在方法中提供要增强的代码。

        6 spring中的AOP

  • AOP的代理选择:默认情况下,Spring会根据被代理对象是否实现接口来选择使用JDK还是CGLIB。当被代理对象没有实现任何接口时,Spring会选择CGLIB。当被代理对象实现了接口,Spring会选择JDK官方的代理技术,不过我们可以通过配置的方式,让Spring强制使用CGLIB。

    6.1 AOP术语

  • Joinpoint(连接点):它指的是哪些可以用于把增强代码加入到业务主线中的点,那么由上图中我们可以看出,这些点指的就是方法。在方法执行的前后通过动态代理技术加入增强代码。在spring框架AOP思想的技术实现中,也只支持方法类型的链接点。

  • Pointcut(切入点):它指的是哪些已经把增强代码加入到业务主线进来之后的连接点。
  • Aspect(切面):它指定的是增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面类。在前面的案例中,TrasnactionManager就是一个切面类。
  • Advice(通知/增强):它指的是切面类中用于提供增强功能的方法。并且不同的方法增强的时机是不一样的。比如开启事务肯定要在业务方法之前执行;提交事务要在业务方法正常执行之后执行,而回滚事务要在业务方法执行产生异常之后执行等等。那么这些就是看通知的类型。其分类有:前置通知,后置 通知,异常通知,最终通知,环绕通知。
  • Target(目标对象):它指的是代理的目标对象。即代理对象。
  • Proxy(代理):它指的是一个类被AOP织入增强后,产生的代理类。即代理对象。
  • Weaving(织入):它指的是吧增强应用到目标来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
  • Introduction(引介):它指的是在不修改类代码的前提下,运行期为目标对象动态地添加一些方法或字段。

    6.2 AOP配置的方式:和IOC配置一样支持三类

    1.XML:基于XML的AOP配置细节

  • 关于切入点表达式

    • 在入门案例中,我们实现了对AcountServiceImpl的save方法进行增强,在其执行之前,输出了记录日志的语 句。这里面,我们接触了一个比较陌生的名称:切入点表达式。
    • 概念及作用:切入点表达式,也称之为Aspect切入点表达式,指的是遵循特定语法结构的字符串,其作用是用于对符合语法格式的连接点进行增强。是Aspect表达式的一部分。
    • 关于AspectJ:是一个基于Java语言的AOP框架,spring框架2.0版本之后集成了AspectJ框架中切入点表达式的部分,开始支持AspectJ切入点表达式。
    • 表达式中关键字:execution,用于匹配方法执行的连接点。
    • 切入点表达式的使用实例:
      • 全限定方法名:访问修饰符 返回值 包名.包名.包名.类名.方法名(参数列表)
      • 全匹配方式:public void com.bai.service.impl.AccountServiceImpl.saveAccount()
      • 访问修饰符可以省略:void com.bai.service.impl.AccountServiceImpl.saveAccount()
      • 返回值可以使用 * ,表示返回任意类型:

* com.bai.service.impl.AccountServiceImpl.saveAccount()

  1. - 包名可以使用 .. 表示当前包及其子包:**_* com.bai.service..AccountServiceImpl.saveAccount()_**
  2. - 类名和方法名,都可以使用 * ,表示任意类和任意方法:**_* com.bai.service.impl.*.*()_**
  3. - 参数列表,可以使用具体类型:基础类型直接写类型名称如:int 。引用类型必须写全限定类名:java.lang.String
  4. - 参数列表可以使用 * 表示任意参数类型,但是必须有参数:
  5. **_ * com.bai.service.impl.AccountServiceImpl.saveAccount(*)_**
  6. - 参数列表可以使用 .. ,表示有无参数均可,参数可以是任意类型:
  7. **_ * com.bai.service.impl.AccountServiceImpl.saveAccount(..)_**
  8. - 全通配方式:**_* *..*.*(..)_**
  9. - 开发中常用方式:**_* com.bai.service..*.*(..)_**
  • 标签介绍
    • aop:config
      • 作用:用于表示开始aop配置
      • 出现位置:写在beans标签的内部
      • 属性:
        • proxy-target-class:用于指定代理方式。默认值是false。当取值为true时,采用是cglib方式。
        • expose-proxy:用于指定是否暴露代理对象,通过AOPContext可以进行访问代理对象。
    • aop:aspect
      • 作用:用于配置切面
      • 出现位置:aop:config 标签内部
      • 属性:
        • id:用于指定切面的唯一标识
        • ref:用于指定引用bean的id
        • order:用于指定多个切面中,相同通知类型的执行顺序。取值是个整数,数值越小越优先。
    • aop:pointcut
      • 作用:用于配置通用切入点表达式
      • 出现位置:
        • aop:config标签内部,当出现在此处时,要求必须在所有aop:aspect标签之前。他可以供所有切面使用。
        • aop:aspect标签内部,当出现在此处时,他没有顺序要求,但只能供当前切面使用。
  • 配置SpringAOP使用Cglib代理模式
    • Spring在选择创建代理对象时,会根据被代理对象的实际情况来选择。被代理对象实现了接口,则采用基于接口的动态代理。当被代理对象没有实现任何接口的时候,Spring会自动切换到基于子类的动态代理方式
    • 无论被代理对象是否实现接口,只要不是final修饰的类都可以采用cglib提供的方式创建代理对象。所以Spring也考虑到了这个情况,提供了配置的方式实现强制使用基于子类的动态代理(即cglib的方式),配置方式有两种:
      • 使用aop:config标签配置,
      • 使用aop:aspectj-autoproxy标签配置,此标签是基于XML和注解组合配置时必备标签,表示Spring开启注解配置AOP支持。

2.XML+annotation
3.annotation:注解详解

  • @EnableAspectJAutoProxy 用于开启注解AOP支持的
  • @PointCut 用于配置切入点表达式的
  • @Aspect 用于配置切面的
  • 用来配置通知的:@Before、@AfterReturning、@AfterThrowing、@After、@Around

    6.3 AOP中要明确的事情

  • 开发阶段(我们做的)

    • 编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务要求
    • 把公用代码抽取出来,制作成通知:AOP编程人员来做
  • 运行阶段(spring框架完成的)
    • Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
  • 五种通知状态:前置通知

    • 配置方式:aop:before 标签
      • 作用:用于配置前置通知
      • 出现位置:只能出现在aop:aspect 标签内部
      • 属性:
        • method:用于指定前置通知的方法名称
        • pointcut:用于指定切入点表达式
        • printcut-ref:用于指定切入点表达式的引用
    • 执行时机:前置通知永远都会在切入点方法(业务核心方法)执行之前执行。
    • 细节:前置通知可以获取切入点方法的参数,并对其进行增强

      7 spring中的事务

  • 四大特性(ACID)

    • 原子性(atomicity):一个事务内的操作,要么都成功,要么都失败。
    • 一致性(consistency):指的hi数据的一致性,和原子性其实是一件事情,只不过描述的角度不一样,原子性是从事务的操作的角度,一致性是从数据的角度来描述的,比如转账之前(1000,1000),如果转账100,那么数据状态应该是(900,1100),不应该出现中间状态(900,1000)或者(1000,1100)
    • 隔离性(isolation):事务并发的时候,比如事务1做的动作是给员工涨工资2000块,但是此时会务还没有提交,事务2去查询工资的时候发现工资多了2000块,这就是脏读。解决方法是建立事务之间的隔离机制。
    • 持久性(durability):事务一旦提交,变化立即生效。即使数据库服务器宕机,那么恢复之后,数据也应该是事务提交之后的状态,不应该回滚到以前了
  • 关于事务并发问题

    • 脏读(读到了未提交的事务):例如:财务发起事务1给张三涨了1W工资,但是还没提交;张三发起事务2查询工资,发现工资涨了,财务发现不对把事务1操作回滚
    • 幻读(出现在insert和delete的时候):例如:事务1查询工资表中工资为1W的有十个,此时事务1还没结束;正在这个时候,事务2,人力部有两个员工新入职工资恰好为1W,人力部门通过事务2向工资表插入了这两条数据,并且提交了事务。这个时候事务1又去查询工资为1W的个数,发现多了两个,变成了12个,见鬼了,这就叫幻读
    • 不可重复读(出现在update的时候):事务1发起查询工资,此时事务1还没结束;人力部门发起事务2给你涨工资到1.2W(update你的工资信息),并且提交了事务。此时,事务1再次查询自己的工作,发现为1.2W了,原有的1W数据已经读取不到了,这就叫不可重复读。
  • 事务隔离级别(解决事务并发问题)

    • 极端模式:读未提交Read_uncommited,就好比十字路口没有红绿灯一样,效率高,但是风险也高,此时什么事务控制也没有。(不要使用这个模式,脏读,幻读,不可重复读都可能发生)
    • 读已提交Read_commited:顾名思义,其他事务提交之后,才能读取这个事务提交数据,这个模式能解决脏读问题,解决不了幻读和不可重复读。(Oracle默认)
    • 可重复读Repeatable_Read:可以进行数据重复读,解决了脏读和不可重复读的问题。(MySQL默认)
    • 极端模式:串行化,所有的事务一个个来,不争不抢,一个事务处理完了,另一个事务继续进行,这样不会出现并发问题,比如ATM机。(避免脏读,幻读,不可重复读发生)
    • 默认,default:默认是数据库的默认,默认模式来源于上面的四种模式之一
    • 查看当前事务隔离级别:select @@tx_isolation
  • 关于事务传播行为:我们的事务往往加载在service层方法上,那么我们现在的业务简单些,直接service调用dao层方法,以后可能涉及service层方法A()直接调用service层方法B()。那么此时A()和B()都有自己的事务控制,那么相互调用的时候就会有问题,A和B应该有一个关于事务的协商机制,这种机制就叫做事务的传播行为

    • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
    • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
  • spring中事务APIPlatformTransactionManager 接口

  • 事务相关配置标签属性

    • 事务配置通知标签
      • 属性id:自定义唯一标识
      • transaction-manager属性:事务管理类,配置事务管理类的id属性值
    • 事务属性配置子标签:
      • name:方法名
      • read-only:是否只读事务,查询都是只读,其他是非只读;默认配置为false
      • propagation:事务的传播行为,默认配置REQUIRED或者SUPPORTS
      • isolation:事务隔离级别,默认配置DEFAULT
      • timeout:事务超时时间,配置-1
      • no-rollback-for:遇到什么异常不回滚,配置异常类名,多个类逗号分开
      • rollback-for:遇到什么异常回滚。若这两个都不配置,那么遇到异常就会回滚
    • aop切面配置标签,子标签
      • advice-ref:引用通知,配置tx:advice标签的属性值
      • 属性pointcut:切点配置
  • 事务相关注解

    • @EnableTransactionManager 开始事务管理注解扫描
    • @Transactional 放在类上就是给当前类里面所有方法加上事务,也可以只放在一个方法上。