Spring基础

没有我灵魂,学习阶段复习使用(文档源码为主)


spring是什么

spring是于2003年兴起的一个轻量级的Java开发框架,它是为了解决企业应用开发的复杂性而创建的。主要优势之一就是它的分层架构,分层架构允许使用者选择使用哪一个组件。使用Spring可以让简单的javaBean实现之前只有EJB才能完成的事情。但spring不仅仅局限于服务端开发,任何java引用都能在简单性,可测试性和松耦合等方面从 spring中获益。

Spring的核心是控制反转(IOC)和面向切面(AOP)。简单来说,Spring是一个分层的轻量级开源框架

开发分层可分为三层结构:

WEB层:SpringMVC

业务层:Bean管理(IOC)

持久层:Spring的JDBC模板,ORM模板用于整合其他的持久层框架

Spring的优点

  1. spring属于低侵入式设计,代码的污染极低
  2. spring的DI机制降低了业务对象替换的复杂性
  3. 容器提供了AOP技术,利用它很容是显现如权限拦截,运行期间监控等功能
  4. 降低了组件之间的耦合性,实现了软件各层之间的解耦
  5. 容器提供单例模式支持
  6. 可以使用容器提供的众多服务,如事务管理,消息服务等;
  7. 容器提供了众多的辅助类,能加快应用的开发
  8. spring对于主流的应用框架提供了继承支持
  9. 独立于各种应用服务器
  10. spring的高度开放性,并不强制应用完全依赖与spring,开发这可以自由选择spring的部分和全部
  11. Dependency Injection(DI)方法使得构造器和JavaBean properties文件中的依赖关系一目了然
  12. 与EJB容器相比,IOC容器更加趋向于轻量级。这样一来IOC容器在有限的内存和CPU资源的情况下进行程序的开发和发布就变得十分有利
  13. Spring并没有闭门造车,Spring利用了以有的技术比如ORM框架、logging框架、J2EE、Quartz和JDK Timer,一起其他视图技术
  14. spring框架是按照模块的形式来组织的,由包和类的编号就可以看出其所属的模块,开发者仅仅需要选用他们需要的模块即可。
  15. 要测试一项用Spring开发的应用程序十分简单,因为测试相关的环境代码都已经囊括在框架中了,更加简单的是,利用javabean形式的pojo类,可以很方便的利用依赖注入来写入测试数据。
  16. spring的web框架亦是一个精心设计的web MVC框架
  17. spring提供了一个边界的事务管理接口,适用于小型的本地事务处理(比如在但DB的环境下)和复杂的共同事务处理(比如利用JTA的复杂DB环境)

什么是控制反转(IOC)?什么是依赖注入(DI)

  1. IOC就是控制反转。将对象的创建权反转交给Spring,由容器控制程序之间的依赖关系,作用是实现了程序的解耦合,而非传统实现中,由程序代码直接操控。(依赖)控制权由应用代码本身转到外部容器,由容器根据配置文件去创建实例并管理各个实例之间的依赖关系,控制权的转移,是所谓反转,并且由容器动态的将某种依赖关系注入到组件之中。BeanFactory是SrpingIOC容器的具体实现与核心接口,提供了一个先进的配置机制,使得任何类型的对象的配置成为可能,用来包装和管理各种bean。
  2. 最直观的表达就是:IOC让对象的创建不用去new了,可以由spring自动生产,这里用的就是java的反射机制,通过反射在运行时动态的去创建、调用对象。spring就是根据配置文件在运行时动态的去创建对象,并调用对象的方法。
  3. spring的依赖注入的四种方式
    • 根据属性注入,set注入:对于习惯了传统 javabean 开发的程序员,通过 setter 方法设定依赖关系更加直观。如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时设值注入模式则更为简洁。如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造子注入模式也不适用。
    • 构造方法注入:在构造期间完成一个完整的、合法的对象。所有依赖关系在构造函数中集中呈现。依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。
    • 注解注入:使用注解注入依赖对象不用再在代码中写依赖对象的setter方法或者该类的构造方法,并且不用再配置文件中配置大量的依赖对象,使代码更加简洁,清晰,易于维护。
    • 接口注入:接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限。

总体说:

  1. IOC,控制反转:将对象交给容器管理,你只需要在spring配置文件中配置相应的bean,以及设置相应的属性,让spring容器生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化以及装配好,然后在你需要调用的时候,就把他已经初始化好的那些bean分配给你需要调用这些bean的类。就是将对象的控制反转给spring容器管理。
  2. DI(Dependency Injection ,依赖注入):可以说是IOC的其中一个内容,在容器实例化对象的时候主动的将被调用这(或者说他的依赖对象)注入给调用对象。

IOC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

BeanFactory和ApplicationContext有什么区别

BeanFactory和ApplicationContext是Spring的两大核心接口,而其中ApplicationContext是BeanFactory的子接口。他们都可以当作Spring的容器,生成Bean实例,并管理容器中的Bean。

  1. BeanFactory:是Spring里面最底层的接口,提供了最简单的容器的功能,负责读取bean配置文档,管理bean的加载与实例化,维护bean之间的依赖关系,负责bean的生命周期,但是无法支持spring的aop功能和web应用。
  2. ApplicationContext接口作为BeanFactory的派生,因而具有BeanFactory的所有功能,而且ApplicationContext还在功能上做了扩展,以一种更面向框架的方式工作以及对上下文进行分层和实现继承,相较于BeanFactory,ApplicationContext还提供了以下的功能:
    • 默认初始化所有的Singleton,也可以通过配置取消初始化
    • 继承MessageSource,因此支持国际化
    • 事件机制
    • 同时加载多个配置文件
    • 以声明式方式启动并创建Spring容器
    • 载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次
    • beanfactory采用的是延迟加载形式来注入bean的,即只有在使用到某个bean时(调用getBean()),才对该bean进行加载实例化,这样,我们就不能发现一些存在的spring的配置问题。如果bean的某一个属性没有注入,beanfactory加载后,直到第一次使用调用getBean方法才会抛出异常。

    • 而ApplicationContext则相反,它在容器启动时,一次性创建了所有的bean,这样,在容器启动时,我们就可以发现spring中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例bean,通过预载入单实例bean,确保当你需要的时候,你就不用等待了,因为他们已经创建好了。

    • 相对于基本的beanFactory,ApplicationContext唯一的不足是占用了内存空间。当应用程序配置bean较多时,程序启动较慢
  3. beanfactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader
  4. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

添加bean的几种方式

给容器中注册组件;

  1. 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]

  2. @Bean[导入的第三方包里面的组件]

  3. @Import[快速给容器中导入一个组件]

    1. @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
    2. ImportSelector:返回需要导入的组件的全类名数组;
    3. ImportBeanDefinitionRegistrar:手动注册bean到容器中
  4. 使用Spring提供的 FactoryBean(工厂Bean);

    1. 默认获取到的是工厂bean调用getObject创建的对象
    2. 要获取工厂Bean本身,我们需要给id前面加一个&
      ​ &colorFactoryBean

自动装配

Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值;

  1. @Autowired:自动注入:

    1. 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值

    2. 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查applicationContext.getBean(“bookDao”)

    3. @Qualifier(“bookDao”):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
    4. 自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false);
    5. @Primary:让Spring进行自动装配的时候,默认使用首选的bean;也可以继续使用@Qualifier指定需要装配的bean的名字
1
2
3
4
BookService{
@Autowired
BookDao bookDao;
}
  1. Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]

    • @Resource:

      ​ 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;

      ​ 没有能支持@Primary功能没有支持@Autowired(reqiured=false);

    • @Inject:

      ​ 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;

    • @Autowired:Spring定义的; @Resource、@Inject都是java规范

    • AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;

  2. @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值

    1. [标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配

    2. [标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取

    3. 放在参数位置:

  1. 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);

    自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;把Spring底层一些组件注入到自定义的Bean中;

    xxxAware:功能使用xxxProcessor;

    ApplicationContextAware==》ApplicationContextAwareProcessor;

解释spring支持的几种bean的作用域

spring容器中的bean可以分为5个范围。所有范围的名称都是自说明的,但是为了避免混淆,全部解释一下

基本作用域:

  1. singleton: 这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由beanfactory自身来维护
  2. prototype:原型范围与单例范围相反,为每一个bean请求提供一个实例

Web作用域:

  1. request:每一次HTTP请求都会产生一个新的bean,这个bean仅在当前request内有效
  2. session:每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前session内有效
  3. global session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

Spring Bean的生命周期

Servlet的生命周期:实例化,初始化init,接受请求service,销毁destory;

Spring上下文中的bean生命周期也类似,如下:

  1. 实例化一个bean——也就是new

  2. 按照Spring上下文对实例化的Bean进行配置——IOC注入

  3. 如果这个Bean实现了BeanNameAware接口,Spring将bean的ID传递给setBeanName()方法;

  4. 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;

  5. 如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的应用传入进来

  6. 如果bean实现了BeanPostProcessor接口,Spring将调用特们的postProcessBeforeInitialization()方法,

  7. 如果bean实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似地,如果bean使用init-method声明了初始化方法,该方法也会被调用;

  8. 如果Bean配置了init-method方法,则会执行init-method配置的方法,

  9. 如果bean实现了BeanPostProcessor接口,Spring将调用他们的postProcessAfterInitialization()方法;

    以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,档摊在Spring配置文件中也可以配置非Singleton。

  10. 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destory()方法。

  11. 如果这个Bean的spring配置中配置了destory-method属性,会自动调用其配置的销毁方法。(destory-method方法没有参数)

这里描述的是应用spring上下文bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步

Spring中bean的加载过程

  1. 获取配置文件
  2. 对获取的xml资源进行一定的处理检验
  3. 处理包装资源
  4. 解析处理包装过后的资源
  5. 加载提取bean并注册(添加到beanDefinitionMap中)

Spring框架中的单例Beans是线程安全的么

Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发这自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Servier类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如View Model对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”

Spring如何处理线程并发问题

Spring使用ThreadLocal解决线程安全问题

在一般情况下,只有有状态的bean才可以在多线程环境下共享,在Spring中,绝大部分bean都可以声明为singleton作用域。就是因为Spring对一些Bean((如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

(1)在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

(2)而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

(3)概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

Spring自动装配模式的区别

  1. no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系。
  2. byName:该选项可以根据bean名称设置依赖关系。当一个bean中自动装配一个属性时,容器将根据bean的名称自动在配置文件中查询一个匹配的bean。如果找到的话,将装配这个属性,如果没有找到就报错。
  3. byType:该选项可以根据bean类型设置依赖关系。当想一个bean中自动装配一个属性时,容器将根据bean的类型自动在配置文件中查询一个匹配的bean。如果找到的话,将装配这个属性,如果没有找到就报错。
  4. constructor:构造器的自动装配和byType模式类似,但是仅仅适用于与有构造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么将会派出异常。
  5. autodetect:该模式自动探测使用构造器自动装配或者bytype自动装配。首先,尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在bean内部没有找到相应的构造器或者无参构造器,容器就会自动选择byType的自动装配方式。

Spring控制器的加载过程(XML)

  1. Web容器创建
  2. 上下文创建,但未初始化
  3. 监听器创建,并注册到Context上
  4. 上下文初始化
  5. 通知坚挺着,Spring配置文件@Configuration加载
  6. Load-on-startup> 0 的ServletConfig床架,springMVC的DispatcherServlet此时创建

spring容器是SpringMVC的父容器。Spring的AOP在Spring的上下文创建时就会创建;如果想要代理SpringMVC的控制层,需要将配置写道SpringMVC的配置文件下。

Spring框架中用到了那些设计模式

  1. 代理模式——在AOP和remotiong中被用到的比较多
  2. 单例模式——在spring配置文件中定义的bean默认为单例模式
  3. 工厂模式——beanFactory用来创建对象的实例
  4. 模板方法——用来解决代码重复的问题。比如。RestTemplate、JmsTemplate、JpaTemplate
  5. 前端控制器——Spring提供了DispatcherServlet来对请求进行分发
  6. 视图帮助(View Helper)—— Spring提供了一系列的JSP标签,高效将辅助分散的代码整合在视图里
  7. 依赖注入——贯穿于BeanFactory/ApplicationContext接口的核心理念

Spring事务的种类和各自的区别

  1. 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务的管理,spring推荐使用TransactionTemplate
  2. 声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
  3. 显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

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:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作

Spring事务的实现方式和实现原理

  1. 划分处理单元——IOC

    由于spiring解决的问题是对单个数据库进行局部十五处理,具体的实现首先用spring中的IOC划分了事务处理单元,并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设置事务的传播特性及隔离机制)

  2. AOP拦截需要进行事务处理的类

    Spring事务处理模块是通过AOP功能来实现声明事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,生成proxy代理对象,通过TransactionInterceptor完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。

    读取ioc容器事务配置属性,转化为spring事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。

  3. 对事务处理实现(事务的生成、提交、回滚、挂起)

    spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是通过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的支持。为常用数据源支持提供了一系列的TransactionManager。

  4. PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean结合起来,形成一个Spring声明式事务处理的设计体系。

------------- 感谢阅读-------------