Spring之详解

概述

什么是Spring

Spring是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。

Spring最根本的使命:解决企业级应用开发的复杂性,即简化Java开发。
Spring设计目标:Spring为开发者提供一个一站式轻量级应用开发平台;
Spring设计理念:在JavaEE开发中,支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持OO(面向对象)设计方法;Spring通过IOC容器实现对象之间耦合关系的管理,实现解耦,将对象之间的依赖关系交给IOC容器,实现依赖反转;
Spring框架的核心:IOC容器和AOP模块。通过IOC容器管理POJO对象以及它们之间的依赖关系;通过AOP以动态非侵入的方式增强服务。IOC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

为了降低Java开发的复杂性,Spring采取了以下4种关键策略

  • 基于POJO的轻量级和最小侵入性编程;
  • 通过依赖注入和面向接口实现松耦合;
  • 基于切面和惯例进行声明式编程;
  • 通过切面和模板减少样板式代码。

Spring的优缺点

优点:

  • 方便解耦,简化开发
    Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理。
  • AOP编程的支持
    Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
  • 声明式事务的支持
    只需要通过配置就可以完成对事务的管理,而无需手动编程。
  • 方便程序的测试
    Spring对Junit4支持,可以通过注解方便的测试Spring程序。
  • 方便集成各种优秀框架
    Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。
  • 降低JavaEE API的使用难度
    Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。

缺点

  • Spring明明一个很轻量级的框架,却给人感觉大而全
  • Spring依赖反射,反射影响性能
  • 使用门槛升高,入门Spring需要较长时间

Spring框架包含哪些模块

Spring 总共大约有 20 个模块, 由 1300 多个不同的文件构成。 而这些组件被分别整合在核心容器(Core Container) 、 AOP(Aspect Oriented Programming)和设备支持(Instrmentation) 、数据访问与集成(Data Access/Integeration) 、 Web、 消息(Messaging) 、 Test等 6 个模块中。 以下是 Spring 5 的模块结构图:
在这里插入图片描述

  • spring core:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。
  • spring beans:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理的对象称为Bean。
  • spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方法
  • spring jdbc:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析, 用于简化JDBC。
  • spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
  • spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。
  • spring test:主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。

Spring框架中的事件分类

Spring 提供了以下5种标准的事件:

  • 上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
  • 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  • 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  • 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  • 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

Spring的核心

spring设计的核心就是Ioc/DI、AOP。spring所支持的很多功能都是构建在这两者之上的。
Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)。

控制反转(IOC)和依赖注入(DI)

IOC

IOC(控制反转)不是什么技术,而是一种设计思想,实际上就是把对象的生命周期托管到Spring容器中,而反转是指对象的获取方式被反转了。换句话说,控制指的是控制外部资源的获取(不仅仅是对象),反转是指由容器帮我们完成查找和注入需要依赖的对象,而对象只是被动的接受依赖的对象,也就是依赖对象的获取方式反转了。
我们怎么理解这句话呢?
举例来说,传统意义上当A类需要用到B类时,就需要在A类中通过关键new来创建B类的对象,这种方式会使代码之间的耦合度非常高。当使用IOC容器后,A类就不需要通过new来创建B类了,而是直接从IOC容器中直接获取B类对象来使用。
那spring Ioc容器中的对象是什么时候构建的呢?
在spring应用启动时,应用会将通过XML、注解或者JavaConfig方式配置的bean加载到IOC容器中。

控制反转的作用:

  • 管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序员来维护的话,那是相当头疼的
  • 解耦,由容器去维护具体的对象之间的依赖关系
  • 托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,现在程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的

控制反转的优点

  • IOC 或 依赖注入把应用的代码量降到最低。
  • 它使应用容易测试,单元测试不再需要单例和JNDI查找机制。
  • 最小的代价和最小的侵入性使松散耦合得以实现。
  • IOC容器支持加载服务时的饿汉式初始化和懒加载。

DI

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

理解依赖注入的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,分下如下:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器;
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  • 谁注入谁:很明显是IoC容器注入应用程序的某个对象,应用程序依赖的对象;
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

IOC和DI由什么关系呢?
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

还有人这样描述IOC和DI之间的关系,IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。

依赖注入的实现方式

依赖注入有三种实现方式:接口注入(Interface Injection)Setter方法注入(Setter Injection)构造器注入(Constructor Injection)。其中接口注入由于在灵活性和易用性比较差,从Spring4开始已被废弃。不过现在基本都是基于注解的方式来描述Bean之间的依赖关系,比如@Autowired、@Inject和@Resource。

  • 构造器注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
  • Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。

构造器注入和Setter注入的区别

构造器注入Setter注入
没有部分注入有部分注入
不会覆盖 setter 属性会覆盖 setter 属性
任意修改都会创建一个新实例任意修改不会创建一个新实例
适用于设置很多属性适用于设置少量属性

两种依赖方式各有千秋,一般使用构造器参数实现强制依赖,setter方法实现可选依赖。

依赖注入的基本原则

应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(JavaBean中的setter)或者是构造器传递给需要的对象

依赖注入有什么优势

依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:

  • 查找定位操作与应用代码完全无关。
  • 不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
  • 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。

Bean的装配方式

随着spring的发展,bean的装配方式也在发生变化,主要也是为了简化bean装配的过程,提高bean装配的灵活性。主要有三种方式:XML、注解和JavaConfig。

基于XML配置的方式很好的完成了队形生命周期的描述和管理,但是随着项目规模不断扩大,XML的配置也逐渐增多,变的越来越难管理。

随着JDK1.5带来的注解支持,Spring从2.x开始,可以使用注解的方式来对Bean进行声明和注入,大大减少了XML的配置量。

Spring升级到3.x之后,提供了javaConfig的能力,它可以完全取代XML。需要注意的是如果基于JavaConfig的形式来完成Bean的装载,则必须要使用@Configuration注解及@Bean注解。

面向切面(AOP)

详见Spring之AOP

BeanFactory 和 ApplicationContext的区别

上文中我们知道在Spring中对象的创建以及关系的维护都是有容器完成的,那在Spring中容器具体是怎么实现的呢?答案就是通过BeanFactory 和 ApplicationContext实现的。事实上,它们都是接口,其中ApplicationContext接口继承了BeanFactory接口,由此可知ApplicationContext是BeanFactory 的扩展,比BeanFactory 拥有更多功能。

BeanFactory 简单粗暴,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。

ApplicationContext 可以称之为 “高级容器”,它除了通过ListableBeanFactory接口间接继承 BeanFactory外,还继承了多个其他接口,因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等,所以你看它的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。
在这里插入图片描述
解读一下这张图:
最上面的是 BeanFactory,下面的 3 个绿色的,都是功能扩展接口,这里就不展开讲。
左边灰色区域的是 “低级容器”, 只负责加载 Bean,获取 Bean。容器其他的高级功能是没有的,例如上图画的 refresh 刷新 Bean 工厂所有配置,生命周期事件回调等。
ApplicationContext 粉红色的 “高级容器”,依赖着 “低级容器”,这里说的是依赖,不是继承。它依赖着 “低级容器” 的 getBean 功能。而高级容器有更多的功能:支持不同的信息源头,可以访问文件资源,支持应用事件(Observer 模式)。

BeanFactory

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

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

Spring中通过BeanFactory如何实现IOC容器:

  1. 加载配置文件,解析成 BeanDefinition 放在 Map 里。
  2. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。

当然这只是简单的描述,实际过程远比这复杂,后面会详细介绍。

ApplicationContext

ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

  • 继承MessageSource,因此支持国际化。
  • 统一的资源文件访问方式。
  • 提供在监听器中注册bean的事件。
  • 同时加载多个配置文件。
  • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

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

Spring中通过ApplicationContext如何实现IOC容器:
至于高级容器 ApplicationContext,他包含了低级容器的功能,当他执行 refresh 模板方法的时候,将刷新整个容器的 Bean。同时其作为高级容器,包含了太多的功能。一句话,他不仅仅是 IoC。他支持不同信息源头,支持 BeanFactory 工具类,支持层级容器,支持访问文件资源,支持事件发布通知,支持接口回调等等。

ApplicationContext常见实现

  • ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。
  • FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。
  • WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
    spring上下文和springmvc上下文的关系

Spring中bean的详解

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

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

bean的创建方式

  • XML配置文件
    Spring配置文件是个XML 文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。
  • 基于注解的配置
  • 基于java的配置

基于xml注入bean的几种方式:

  • Set方法注入
  • 构造器注入:①通过index设置参数的位置②通过type设置参数类型
  • 静态工厂注入
  • 实例工厂

bean的作用域

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

  • singleton : bean在每个Spring ioc 容器中只有一个实例。
  • prototype:一个bean的定义可以有多个实例。
  • request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
  • session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring
  • ApplicationContext情形下有效。
  • global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

注意: 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean 会带来很大的性能开销。

如何给Spring中的bean设置作用域呢?

如果使用XML方式创建bean,可以通过bean 定义中的scope属性来定义;

bean的生命周期

详见Spring之bean的生命周期

内部bean

在Spring框架中,当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现,内部bean通常是匿名的,它们的Scope一般是prototype。

bean的自动装配

Spring中有三种装配方式:

  • 在xml中显示配置;
  • 在java中显示配置;
  • 隐式的bean发现机制和自动装配
    spring的自动装配功能的定义:无须在Spring配置文件中描述javaBean之间的依赖关系(如配置、)。IOC容器会自动建立javabean之间的关联关系。
    如果没有采用自动装配的话,手动装配我们通常在配置文件中进行如下实现:
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 
  
  <bean id="customerDAO" class="com.hebeu.customer.dao.JdbcCustomerDAO"> 
    <property name="dataSource" ref="dataSource" /> 
  </bean> 
  
</beans>

通过向customerDAO的bean中注入了dataSource。但是每次都这样装配太麻烦了,所以Spring提供了多种自动装配的方式。

FactoryBean 和 BeanFactory的区别

BeanFactory

BeanFactory定义了IOC容器的最基本形式,并提供了IOC容器应遵守的的最基本的接口,也就是Spring IOC所遵守的最底层和最基本的编程规范。Spring代码中,BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,都是附加了某种功能的实现。
它是个bean 工厂,是一个工厂类(接口), 它负责生产和管理bean的一个工厂,是ioc 容器最底层的接口,是个ioc容器,是spring用来管理和装配普通bean的ioc容器(这些bean成为普通bean)。

spring不允许我们直接操作 BeanFactory ,所以为我们提供了ApplicationContext 这个接口 此接口集成BeanFactory 接口,ApplicationContext包含BeanFactory的所有功能,同时还进行更多的扩展

FactoryBean

一般情况下,Spring通过反射机制利用< bean >的class属性实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的。Spring为此提供了一个FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。

Spring如何解决循环依赖

详见Spring之循环依赖

Spring中注解

详见Spring之常见注解

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值