前言

spring框架从十多年前问世以来,一直致力于简化Java EE应用的开发。如果spring不挑战之前版本的企业级JavaBean(EJB)规范的话,现在EJB规范肯定是不同的样子。spring boot是对spring本身的一种颠覆和革命

Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入DI和面向切面编程AOP。

依赖注入(dependency injection,DI)
面向切面编程(Aspect-oriented programming,AOP)

第一章 Spring之旅

1.1
创建spring的主要目的是用来替代更加重量级的企业级java技术,尤其是EJB。相对于它,spring提供了更加轻量级和简单的编程模型,它增强了简单老式java对象(plain old java object,POJO)的功能,使其具备了之前只有EJB和其他企业级java规范才具有的功能。
随着时间的推移,EJB自身也提供了面向简单POJO的编程模型,现在EJB也采用了依赖注入和面向切面编程的理念,这毫无疑问是收到了spring的启发。

移动开发、社交API集成、Nosql数据库、云计算、大数据都是spring正在涉足和创新的领域。

spring是个开源框架,spring是为了解决企业级应用开发的复杂性而创建的,使用spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。但spring不只局限于服务器端开发,任何java应用都能在简单性、可测试性和松耦合等方面从spring中获益。

bean的各种名称。。。。虽然spring用bean或者JavaBean来表示应用组件,但并不意味着spring组件必须要遵循JavaBean规范。一个spring组件可以是任何形式的POJO。在本书中,作者采用JavaBean的广泛定义,即POJO的同义词。

spring的最根本的使命:简化JAVA开发
那spring如何简化java开发的,采取以下4种关键策略:

  1. 基于POJO的轻量级和最小侵入性编程
  2. 通过依赖注入和面向接口实现松耦合
  3. 基于切面和惯例进行声明式编程。
  4. 通过切面和模板减少样板式代码。
    几乎spring所做的任何事情都可以追溯到上述的一条或多条策略。

**1.1.1激发POJO的潜能 **
很多框架通过强迫应用继承它们的类或实现它们的接口从而导致应用与框架绑死。spring竭力避免因自身的API而弄乱你的应用代码。spring不会强迫你实现spring规范的接口或继承spring规范的类,相反,在基于spring构建的应用中,它的类通常没有任何痕迹表明你使用了spring。最坏的场景是,一个类或许会使用spring注解,但它依旧是POJO。

public class HelloWorldBean{
  public String sayHello(){
  	return "Hello World";
  	}
  }

可以看到这是简单普通的java类——POJO。没有任何地方表明它是一个spring组件。spring的非侵入性编程意味着这个类在spring应用和非spring应用中都可以发挥同样的作用。尽管形式看起来很简单,但POJO一样可以具有魔力。spring赋予POJO魔力的方式之一就是通过DI来装配它们。下面来看看DI是如何帮助应用对象彼此之间保持松散耦合的。

1.1.2 依赖注入
依赖注入现在已经演变成一项复杂的编程技巧或设计模式理念。但事实上,依赖注入并不像它听上去那么复杂,在项目中应用DI,你会发现你的代码变得异常简单并更容易理解和测试。

//代码中DamselRescuingKnight与RescueDamselQuest紧紧耦合在一起
package com.springinaction.knights;

public class DamselRescuingKnight implements Knight {

    private RescueDamselQuest quest;

    public DamselRescuingKnight() {
        quest = new RescueDamselQuest();// 与RescueDamselQuest紧耦合
    }

    @Override
    public void embarhOnQuest() throws QuestException {
        quest.embark();
    }

正如你所见,DamselRescuingKnight 在它的构造函数中自行创建了RescueDamselQuest,这使得DamselRescuingKnight和RescueDamselQuest紧密地耦合到了一起,因此极大地限制了这个骑士的执行能力。
耦合具有两面性。一方面,紧密耦合的代码难以测试、难以复用、难以理解,并且典型地表现出打地鼠式的bug特性(修复一个bug,将会出现一个或者更多新的bug)。另一方面,一定程度的耦合又是必须的-完全没有耦合的代码什么也做不了。不同的类必须以适当的方式进行交互。耦合是必须的,但应当被小心谨慎的管理。
通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。对象无需自行创建或管理它们的依赖关系,依赖关系将被自动注入到需要它们的对象当中去。

package com.springinaction.knights;

public class BraveKnight implements Knight {

    private Quest quest;

    public BraveKnight(Quest quest) {
        this.quest = quest;// quest被注入到对象中
    }

    @Override
    public void embarhOnQuest() throws QuestException {
        quest.embark();
    }

}

不同于之前的DamselRescuingKnight,BraveKnight没有自行创建探险任务,而是在构造器中把探险任务作为参数注入,这也是依赖注入的一种方式,即构造器注入。
BraveKnight中注入的探险类型是Quest,Quest只是一个探险任务所必须实现的接口。因此,BraveKnight能够响RescueDamselQuest、SlayDraonQuest等任意一种Quest实现,这正是多态的体现。

这里的要点是BraveKnight没有与任何特定的Quest实现发生耦合。对它来说,被要求挑战的探险任务只要实现了Quest接口,那么具体是哪一类型的探险就无关紧要了。这就是依赖注入最大的好处–松耦合如果一个对象只通过接口(而不是具体实现或初始化的过程)来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。

对依赖进行替换的一个最常用的方法就是在测试的时候使用mock实现,我们无法充分的测试DamselRescuingKnight,因为它是紧耦合的,但是可以轻松地测试BraveKnight,只需要给它一个Quest的mock实现即可

import static org.mockito.Mockito.*;
import org.junit.Test;

public class BraveKnightTest{
	@Test
	public void kightShouldEmbarkOnQuest(){
		Quest mockQuest = mock(Quest.class); //创建mock quest
		BraveKnight knight = new BraveKnight(mockQuest);//注入mock quest
		knight.embarkOnQuest();
		verify(mockQuest,times(1)).embark();
	}

你可以使用mock框架Mockito去创建一个Quest接口的mock实现,通过这个mock对象,你就可以创建一个新的BraveKnight实例,并通过构造器注入这个mockQuest。当调用embarkOnQuest()方法时,你可以要求Mockito框架验证Quest的mock实现的embark()方法仅仅被调用了一次。
注入一个Quest到Knight

import java.io.PrintStream;
public class SlayDragonQuest implements Quest{
	private PrintStream stream;
	public SlayDragonQuest(PrintStream stream){
		this.stream = stream;
	}
	public void embark(){
		stream.println("Embarking on quest to slay the dragon!");
	}
}

如何将SlayDragonQuest交给BraveKnight?如何将PrintStream交给SlayDragonQuest?
创建应用组件之间协作的行为通常称为装配(wiring)。Spring有多种装配Bean的方式,采用XML是很常见的一种装配方式。
下面是一种简单的装配文件knights.xml,该配置文件将BraveKnight、SlayDragonQuest和PrintStream装配到了一起。

<?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="com.springinaction.knights.BraveKnight">
        <constructor-arg ref="quest"></constructor-arg>
    </bean>

    <bean id="quest" class="com.springinaction.knights.SlayDragonQuest">
		<constructor-arg value="#{T(System).out}"  />
	</bean>

</beans>

BraveKnight和SlayDragonQuest被声明为Spring中的bean。就BraveKnight bean来讲,它在构造时传入了对SlayDragonQuest bean 的引用,将其作为构造器参数。同时,SlayDragonQuest bean的声明使用了spring表达式语言,将system.out(一个PrintStream)传入到了SlayDragonQuest的构造器中。
在SpEL中, 使用T()运算符会调用类作用域的方法和常量. 例如, 在SpEL中使用Java的Math类, 我们可以像下面的示例这样使用T()运算符:

T(java.lang.Math)

T()运算符的结果会返回一个java.lang.Math类对象.

Spring还支持java来描述配置。

import guo.knights.BraveKnight;
import guo.knights.Knight;
import guo.knights.Quest;
import guo.knights.SlayDragonQuest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by guo on 20/2/2018.
 */
@Configuration
public class KnightConfig {
    @Bean
    public Knight knight() {
        return new BraveKnight(quest());
    }
    @Bean
    public Quest quest() {
        return new SlayDragonQuest(System.out);
    }
}

不管使用的是基于XML的配置还是基于Java的配置,DI所带来的收益都是相同的。尽管BraveKnight依赖于Quest,但是它并不知道传递给它的是什么类型的Quest,与之类似,SlayDragonQuest依赖于PrintStream,但是编译时,并不知道PrintStream长啥样子。只有Spring通过他的配置,能够了解这些组成部分是如何装配起来的。这样就可以在不改变 所依赖的类的情况下,修改依赖关系。接下来,我们只需要装载XML配置文件,并把应用启动起来。

public class KnightMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring/knights.xml");    //加载Sprinig应用上下文
        Knight knight = context.getBean(Knight.class);                                       //获取knight bean
        knight.embarkOnQuest();                                                              //使用knight调用方法
        context.close();                                                                     //关闭应用上下文
    }
}

这里的main()方法基于knight.xml文件创建了spring应用上下文。随后他调用该应用上下文获取一个ID为knight的bean。得到Knight对象的引用后,只需要简单调用embarkOnQuest方法就可以执行所赋予的探险任务了。注意这个类完全不知道我们的英雄骑士接受哪种探险任务,而且完全没有意识到这是由BraveKnight来执行,只有knights.xml知道哪个骑士执行哪种探险任务。
下面介绍基于切面进行声明式编程。

1.1.3 应用切面
DI让相互协作的软件组件保持松散耦合,而面向切面编程AOP允许你把遍布应用各处的功能分离出来形成可重用的组件。

/**
 * Created by guo on 20/2/2018.
 * 咏游诗人,作为骑士的一个切面
 */
public class Minstrel {
    private PrintStream stream;

    public Minstrel(PrintStream stream) {
        this.stream = stream;
    }
    public void singBeforeQuest() {
        stream.println("Fa la la ,the Knight is so brabe");      //探险之前调用
    }
    public void singAfterQuest() {
        stream.println("Tee hee hhe,the brave knight " + "did embark on a quest");   //探险之后调用
    }
}

Minstrel只有两个简单的方法的类,在骑士执行每一个探险任务之前,singBeforeQuest()被调用;在骑士完成探险任务之后,singAfterQuest()方法被调用。在这两种情况下,Minstrel都会通过一个PrintStream类来歌颂骑士的事迹,这个类通过构造器注入进来。
但利用AOP,你可以声明咏游诗人西部歌颂骑士的 探险事迹,而骑士本身不直接访问Minstrel的方法

要将Minstrel抽象为一个切面,你所需要做的事情就是在一个Spring配置文件中声明它

<bean id="minstrel" class="guo.knights.Minstrel">
   <constructor-arg value="#{T(System).out}"/>                                 <!--声明Minstrel bean-->
</bean>

<aop:config>
    <aop:aspect ref="minstrel">

       <aop:pointcut id="embark" expression="execution(* * .embarkOnQuest(..))"/>     <!--定义切点-->

        <aop:after pointcut-ref="embark" method="singBeforeQuest"/>                  <!-- 声明前置通知-->

        <aop:after pointcut-ref="embark" method="singAfterQuest"/>                    <!-- 声明后置通知-->

    </aop:aspect>
</aop:config>

在这两种方式中,pointcut-ref属性都引用列名为为“embark”的切入点,该切入点实在前面的元素中定义的,并配置expression属性来选择所应用的通知。表达式的语法采用的是aspectJ的切点表达式语言。

Minstrel仍然是一个POJO,没有任何代码表明它要被作为一个切面使用,其次最重要的是Minstrel可以被应用到BraveKnight中,而BraveKnight不需要显示的调用它,实际上,BraveKnight完全不知道MInstrel的存在

public class KnightAopMain {
   public static void main(String[] args) {
       ClassPathXmlApplicationContext context =
               new ClassPathXmlApplicationContext("spring/minstrel-AOP.xml");
      Knight knight = context.getBean(Knight.class);
      //Knight knight = (Knight) context.getBean("knight");
       knight.embarkOnQuest();
       context.close();
   }
}

输出如下:
Fa la la ,the Knight is so brabe
Embarking on quest to slay the dragon!!,顺便还可以学英语,一举两得。
Tee hee hhe,the brave knight did embark on a quest

1.2 容纳你的bean
在基于Spring的应用中,你的应用对象存在于Spring容器(container)中.Spring负责创建对象,装配它,并管理它们的整个生命周期,从生存到死亡(new 到finalize())。
容器是spring框架的核心。spring容器使用DI管理构成应用的组件,它会创建相互协作的组件之间的关联。这些对象更简单干净、更容易理解,更易于重用并且易于进行单元测试。
spring容器并不是只有一个。spring自带多个容器实现,可以归为两种不同的类型:

  • Bean工厂。由 org.springframework.beans.factory.BeanFactory接口定义的。是最简单的容器。提供基本的DI支持
  • 应用上下文。 由org.springframework.context.applicationContext接口定义的。基于BeanFactory构建,并提供应用框架级别的服务,例如:从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听器。
    应用上下文比Bean工厂更受欢迎。bean工厂对于大多数应用来说太低级了。

1.2.1使用应用上下文
Spring自带了多种应用上下文:

  • AnonotationConfigApplicationContext:从一个或多个基于Java的配置文件类中加载Spring应用上下文
  • AnnotationConfigWebApplicationContext:从一个或多个基于Java配置类加载SpringWeb应用上下文
  • ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源
  • FileSystemXmlapplicationContext:从文件系统下的一个或多个XMl配置文件中加载上下文定义
  • XmlWebapplicationContext:从web应用下的一个或多个XML配置文件中加载上下文定义

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

加载一个FileSystemXmlApplicationContext: 在文件系统的路径下查找knight.xml

ApplicationContext context =
        new FileSystemXmlApplicationContext("c:/knight.xml");

也可以使用ClassPathXmlApplicationContext: 所有的类路径下查找knight.xml

ApplicationContext context =
        new ClassPathXmlApplicationCOntext("knight.xml");

这两种方式的区别在于,前者是在指定的文件系统路径下查找knight.xml文件,后者是在所有的类路径下查找knight.xml
也可以从Java配置中加载应用上下文,那么可以使用AnnotationConfigApplicationContext

ApplicationContext context = new  AnonotationConfigApplicationContext(
          com.guo.knights.config.KnightConfig.class);

应用上下文准备就绪之后,我们就可以调用上下文的getBean()方法从Spring容器中获取bean。

1.2.2 bean的生命周期
在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后bean就可以使用了。
一旦bean不再使用,则由Java自动进行垃圾回收。
相比之下,Spring容器中的bean声明周期就显得复杂多了。正确理解Spring bean的生命周期非常重要,因为你或许要利用Spring提供的扩展点来自定义bean的创建过程。

在bean准备就绪之前,bean工厂执行了若干启动步骤:

  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-mothod声明销毁方法,该方法也会被调用。
1.3 俯瞰Spring风景线

spring通过DI、AOP、消除模板样式代码来简化企业级Java开发。即使这是Spring所能作的全部事情,那么Spring也值得一用,Spring实际上的功能超乎你的想象。

1.3.1 Spring 模块

这些模块依据其所属的功能划分为6类不同的功能,总而言之,这些模块为开发企业及应用提供了所需的一切 。但是你也不必将应用建立在整个Spring框架上,你可以自由的选择合适自身应用需求的Spring模块:当Spring不能满足需求时,完全可以考虑其他选择,事实上,Spring甚至提供了与其他的第三方框架和类库的集成点,这样你就不需要自己编写代码了。

spring zeebe 实战_spring


Spring核心容器

容器是Spring最核心的部分,它管理者Spring应用中bean的创建、配置、管理。在该模块中,包括了Spring bean工厂,它为Spring提供了DI的功能,甚至bean工厂,我们还会发现有多种Spring应用上下文的实现,每一种都提供了配置Spring的不同方式。

除了bean工厂和应用上下文,该模块也提供了许多企业服务,例如E-mail、JNDI访问、EJB集成和调度。

所有的Spring模块都构建于核心容器之上。

Spring的AOP模块

在AOP模块中,spring对面向切面编程提供了丰富的支持。这个模块是spring应用系统中开发切面的基础。与DI一样,AOP可以帮助应用对象解耦。

数据访问与集成

使用JDBC编写代码通常会导致大量的样式代码,Spring的JDBC和DAO(Data Access Object)模块抽象类这些样板代码,是我们的数据库代码变得简单明了。还可以避免因为关闭数据库资源失败而引发的问题。该模块在多种数据库服务的错误信息之上构建了一个语义丰富的异常层,以后我们再也不需要解释那些隐晦专有的SQL信息了。

对于那些更喜欢ORM(Object-Relational Mapping)工具而不愿意直接使用JDBC的开发者,Spring提供了ORM模块,Spring的ORM模块建立在DAO的支持之上,并为多个ORM框架提供了一种构建DAO的简便方式 。Spring没有尝试去创建自己的ORM解决方,而是对许多流行的ORM框架进行了集成。包括Hibernater、Java Persisternce API、Java Data Object 和mybatis。Spring的事务管理支持所有的ORM框架以及JDBC。
WEB与远程调用
MVC(Model-View-Controller)模块是一种普遍被接受的构建Web应用的方法,它可以帮助用户将界面逻辑与应用逻辑分离,Java从来不缺少MVC框架,Apache的struts2、JSF、WebWorks都是可选的最流行的MVC框架。虽然spring能够与多种流行的MVC框架进行集成,但它的web和远程调用模块自带了一个强大的MVC框架,有助于web层提升应用的松耦合水平。
除了面向用户的web应用,该模块还提供了多种构建于其他应用交互的远程调用方法。Spring远程调用功能集成了RMI(Remote mehtod Invocation)、Hessian、CXF。同时spring还自带了一个远程调用框架:HTTP invoker 。Spring还提供了暴露和使用RESTAPI的良好支持。

Instrumentation
Spring的Instrumentation模块提供了为JVM添加代理(agent)的功能.具体来讲,它为Tomcat提供了一个织入代理,能够为Tomcat传递类文件,就像这些文件时被类加载器加载的一样。
测试
spring提供了测试模块以致力于spring应用的测试。
通过该模块,你会发现Spring为JNDI、Servlet和Portlet编写单元测试提供了一系列的mock对象事项,对于集成测试,该模块为加载Spring应用上下文中的bean集合以及与Spirng上下文中的bean进行交互提供了支持。

1.3.2 Spring Portfolio

spring远不是spring框架所下载的那些。如果仅仅停留在核心的Spring框架层面,我们将错过Spring Portfolio所提供的巨额财富。整个Spirng Portfolio包括多个构建于核心Spring框架之上的框架和类库。概括的来讲,整个Spring Portfolio几乎为每一个领域的Java开发都提供了Spring编程模型

  1. Spring Web Flow:是建立于Spring MVC框架之上,它为基于流程的会话式Web应用(购物车、向导功能)提供了支持
  2. Spring Web Service:
  3. Spirng Security:安全度与许多应用都是一个非常关键的切面。利用SpringAOP,SpringSecurity为Spring应用提供了声明式的安全机制。
  4. spring integration:它提供了多种通用应用集成模式的spring声明式风格实现
  5. Spring Batch:如果需要开发一个批处理应用,可以通过Spring Batch。
  6. Spring Data:使得在Spring中使用任何数据库都变得非常容易。不管你使用文档数据库,如MongoDB,图数据库,如Neo4j,还是传统的关系型数据库,Spring Data都为持久化提供了一种简单的编程模型。这包括为多种数据库提供了一种自动化的Repository机制,它负责为你创建Repository的实现
  7. Spring Social:它能帮你通过REst Api连接Facebook或Twitter。
  8. Spring Mobile:它是Spring MVC新的扩展模块,用于支持移动web应用开发。
  9. Spring for Android:旨在通过spring框架为开发基于Android设备的本地应用提供某些简单的支持
  10. Spring Boot: Spring极大的简化了众多编程的任务,减少甚至消除了很多样板式代码。Spring Boot致力于简化spring本身,它大量依赖于自动配置技术,它能够消除大部分Spring配置。还提供了多个Starter项目,不管你是用Maven还是Gradle,这都能减少Spring的工程构建文件的大小

1.4 Spring的新功能
1.4.1 Spring3.1新特性
Spring 3.1带来了多项有用的新特性 和增强,其中有很多都是关于如何简化个改善配置的。除此之外,Spring3.1还提供了声明式缓存的支持以及众多针对SpringMVC的的功能增强。

借助于profile,就能根据应用布置在什么环境之中选择不同的数据源
基于Java配置,Spring3.1增加了多个enable注解,启用Spring特定功能
对声明式缓存的支持,能够 使用简单的注解声明缓存的边界和规则,
开始支持Servlet3.0,包括在基于Java的配置中 申明Servlet和Filter,而不再借助于web.xml
1.4.2 Spring 3.2新特性
Spring 3.2主要关注Spring MVC的一个发布版本。

Spring MVC 3.2带来如下的功能提升

控制器(Controller)可以使Servlet3.0异步请求,允许在一个独立的线程中处理请求,从而就爱那个Servlet线程解放出来处理更多的请求
@Autowired、@Value、@Bean注解能够作为元注解。用于创建自定义的注解和bean的申明注解
Spring的声明式缓存提供了对JCache0.5的支持。
1.4.3 Spring 4.0新特性
Spring提供了对WebSocket编程的支持,
新的消息模块,
支持Java8的新特性,比如:Lambda,函数式,
为 Groovy开发的应用程序提供了更加顺畅的编程体验
添加了条件化创建bean的功能
Spring4.0包含了Spring RestTemplate的一个新的异步实现。它会立即返回并且允许在操作完成后执行回调
添加了对多项JEE规范的支持,包括JMS 2.0 、JTA1.2 JPA 2.1
1.4.4 Spring 5.0新特性
在Spring Framework代码中使用JDK 8特性
响应式编程是Spring Framework 5.0最重要的功能之一
除了响应式特性之外,Spring 5还提供了一个函数式Web框架。
Spring Framework 5.0 引入了对 JetBrains Kotlin 语言的支持。
1.5 小节
Spring致力于简化企业级开发Java开发、促进代码的松耦合。成功的关键在于依赖注入和AOP。

DI是组装应用对象的一种方式,借助于这种方式对象无需知道依赖来自于何处或者依赖的具体实现方式。不同于自己获取依赖对象,对象会在运行期赋予它们所依赖的对象。依赖对象通常会通过接口了解所注入的对象,这样的话就能确保低耦合。

除了DI,还简单介绍了Spring对AOP的支持,AOP可以帮助应用将散落在各处的逻辑汇集于一处——切面。当Spring装配bean的时候,这些切面能够运行期编织起来,这样就能呢个非常有效的赋予bean新功能。

依赖注入和AOP是Spring框架最核心的部分,只有理解了如何应用Spring是最关键的功能。你才有能力使用Spring框架的其他功能。