Spring之简介

本篇是对Spring框架的简介,部分内容总结摘抄自《Spring实战》,仅作笔记。

Spring是一个为了解决企业级应用开发的复杂性而创建的开源框架。为了降低Java开发的复杂性,Spring采取了以下4中关键策略:

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

几乎Spring所做的任何事情都可以追溯到上述的一条或多条策略。

控制反转(Inversion of Control)

控制反转是一种设计思想。在一般的Java程序中,通常使用new来主动创建对象,使用时也是主动去获取依赖对象。在IOC中,由容器来控制对象的声明周期和对象间的关系。这样对象与对象之间的紧耦合就不存在了。

我们需要做的就只是向Spring注册所有的类,并且告诉Spring这个类信息以及依赖对象等,Spring会在需要的时候提供依赖对象以及将这个类的对象提供给其他对象。

依赖注入(Dependency Injection)

IOC的一个重点是在系统运行中,动态的向某个对象提供它所依赖的对象。这一点是通过依赖注入实现的。

任何一个有实际意义的应用都会由两个或更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。按照传统的做法,每个对象负责管理与自己相互协作的对象的引用,这将会导致高度耦合和难以测试的代码。例如下面的Knight类:

public class DamselRescuingKnight implements Knight{
    private RescueDamelQuest quest;

    public DamselRescuingKnight() {
        this.quest = new RescueDamelQuest();
    }

    public void embartOnQuest(){
        quest.embark();
    }
}

在DamselRescuingKnight的构造函数中自行创建了RescueDamelQuest,这使得DamselRescuingKnight紧密的和RescueDamelQuest耦合到了一起,因此极大的限制了DamselRescuingKnight类的能力。

耦合具有两面性。一方面,紧密耦合的代码难以测试、难以复用、难以理解,并且修复一个bug,可能会出现一个或多个bug。另一方面,一定程度的耦合又是必须的,因为完全没有耦合的代码什么也做不了。为了完成有实际意义的功能,不同的类必须以适当的方式进行交互。

通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候设定。对象无需自行创建或管理它们的依赖关系,依赖关系将被自动注入到需要它们的对象中去。例如修改以上代码如下:

public class DamselRescuingKnight implements Knight{
    private Quest quest;

    public DamselRescuingKnight(Quest quest) {
        this.quest = quest;
    }

    public void embartOnQuest(){
        quest.embark();
    }
}

与前面的DamselRescuingKnight相比,这一个DamselRescuingKnight在构造函数中没有选择自行创建RescuDamelQuest,而是将RescuDamelQuest的父接口Quest作为参数传入。这是依赖注入的方式之一,即构造器注入(constructor injection)。

对于DamselRescuingKnight没有任何特定的Quest发生耦合,只要实现了Quest接口即可。这就是DI带来的松耦合。如果一个对象只通过接口来表明依赖关系,那么这种依赖能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。

创建应用组件之间协作的行为通常称为装配。Spring有多种装配bean的方式,采用XML是一种很基本的方式。下面是一个简单的Spring配置文件,该配置文件将DamselRescuingKnight与RescuDamelQuest装配到了一起。

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd">
    <bean id="knight" class="DamselRescuingKnight">
        <constructor-arg ref="quest"/>
    </bean>
    <bean id="quest" class="RescueDamelQuest">
    </bean>
</beans>

DamselRescuingKnight和RescuDamelQuest被声明为Spring中的bean。对于DamselRescuingKnight,它在构造时传入了对RescuDamelQuest的引用,将其作为构造器参数。

还可以使用Java来描述配置,功能与使用xml配置相同。如下:

@Configuration
public class KnightConfig {
    @Bean
    public Knight knight(){
        return new DamselRescuingKnight();
    }
    @Bean
    public Quest quest(){
        return new RescueDamelQuest();
    }
}

在后面会详细介绍关于自动装配bean,此处不再赘述。不管使用哪种方式装配,DI所带来的收益都是相同的。尽管DamselRescuingKnight依赖于RescuDamelQuest,但它并不知道传递给它的是什么类型的Quest,也不知道这个Quest来自哪里。只有Spring通过它的配置,能够了解这些组成部分是如何装配起来的,在不改变所依赖的类的情况下,我们就可以修改依赖关系。

容器

在基于Spring的应用中,应用对象生存于Spring容器中。Spring容器负责创建对象、装配对象、配置对象以及管理它们的整个生命周期。容器是Spring框架的核心。Spring容器使用DI管理构成应用的组件,它会创建相互协作的组件之间的关联。

Spring自带了多个容器实现,可以归为两种不同的类型。一种是bean工厂(由org.springframework.beans.factory.BeanFactory接口定义)是最简单的容器,提供基本的DI支持。另一种是应用上下文(由org.springframework.context.ApplicationContext接口定义)基于BeanFactory构建,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者等。

Spring自带了多种类型的应该上下文,常用的如下:

  • AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文。
  • AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下文。
  • ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。
  • FileSystemXmlApplicationContext:从文件系统下的一个或多个XML配置文件中加载上下文定义。
  • XmlWebApplicationContext:从Web应用下的一个或多个XML配置文件中加载上下文。

无论是从文件系统中装载应用上下文还是从类路径下装载应用上下文,将bean加载到bean工厂的过程都是相似的。例如:

ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

bean的生命周期

在传统的Java应用中,bean的生命周期很简单。使用new进行bean实例化,然后使用bean,一旦该bean不再被使用,则被Java自动进行垃圾回收。

相比之下,Spring容器中的bean的生命周期就显得相对复杂多了。下图展示了bean装载到Spring应用上下文中的一个典型的生命周期过程。

对以上过程的详细描述如下:

  1. Spring对bean进行实例化;
  2. Spring将值和bean的引用注入到bean对应的属性中;
  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实现了BeanPostProcessor接口,Spring调用postProcessAfterInitialization()方法;
  9. 此时bean已经准备就绪,可以被应用程序使用,它们将一直驻留在应用上下文中,知道该应用上下文被销毁;
  10. 如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法,类似的,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。

bean的作用域

在默认情况下,Spring应用上下文中所有bean都是以单例的形式创建的。也就是说,不管给定的一个bean被注入到其他bean多少次,每次注入的都是一个实例。

在大多数情况下,单例bean是很理想的方案。初始化和垃圾回收对象实例所带来的成本只留给一些小规模任务,在这些任务中,让对象保持无状态并且在应用中反复重用这些对象可能并不合理。

有时可能会发现我们使用的类是易变的,它们会保持一些状态,因此重用是不安全的。在这种情况下,将class声明为单例的bean就不是好的办法,因为对象会被污染。Spring定义了多种作用域,可以基于这些作用域创建bean,包括:

  • 单例(Singleton):在整个应用中,只创建bean的一个实例。
  • 原型(Prototype):每次注入或通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
  • 会话(Session):在Web应用中,为每个会话创建一个bean实例。
  • 请求(Request):在Web应用中,为每个请求创建一个bean实例。

单例是默认的作用域。如果要选择其他的作用域,可以使用@Scope注解,它可以与@Component注解或@Bean注解一起使用。

Spring模块

Spring的模块根据其所属的功能可以划分为6个不同的功能,如下图所示:

以上模块的描述如下:

  • 容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建、配置和管理。例如核心容器中的core和beans模块提供了IOC和DI功能,Context提供了框架式访问对象的方式等等。
  • AOP,即面向切面编程。与DI一样,AOP可以帮助应用对象解耦。借助于AOP,可以将遍布系统的关注点从它们所应用的对象中解耦出来。
  • 数据访问与集成使用Spring AOP模块为Spring应用中的对象提供事务管理服务。
  • Web与远程调用。MVC模式是一种普遍被接受的构建Web应用的方法,它可以帮助用户将界面逻辑与应用逻辑分离。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值