第1部分:Spring框架概述

  Spring框架是一个轻量级的解决方案和一个潜在的为构建企业级应用程序而准备的一站式平台。然而,Spring是模块化的,它允许你只使用你需要的那些模块,无需引入不必要的模块。你可以使用IoC容器,在其上面使用任何Web框架(译注:如Struts)。但是你也可以只使用Hibernate集成代码或者JDBC抽象层。Spring框架支持声明式事务管理,通过RMI或者web services远程访问你的逻辑,以及支持各种数据持久化的选项。它提供一个全功能的MVC框架,并且使你能够透明地集成AOP到你的软件中。

  Spring是被设计成非侵入性的,意味着你的领域逻辑代码通常不会依赖于框架本身。在你的集成层(比如数据访问层),会存在一些对数据访问技术和Spring库文件的依赖。然而,在你的代码库的其余部分中这些依赖关系很容易隔离出来。

    这部分文档是一个Spring框架功能特性的参考指南。如果你有任何要求、意见或者对这个文档有任何问题,请发送到用户邮件列表或者支持论坛上http://forum.spring.io/


1. Spring入门指南


  该参考指南提供了关于Spring的详细信息。它为所有的功能提供了全面的文档,以及一些Spring底层概念的背景(比如“Dependency Injection”即依赖注入)。
  如果你刚开始使用Spring,你可能需要从较轻的入门指南开始学习,可以从http://spring.io上下载。除了更加容易学习消化之外,这些指南都聚集于某个特定的任务。他们还涵盖了当你解决特定问题时,可以考虑的Spring的其他项目。


2. Spring框架介绍


  Spring框架提供了广泛的基础设施以支持开发Java应用。Spring自己控制基础设施,这样你就可以专注于你的应用程序。
  Spring使你可以用“简单Java对象”(POJOs)构建应用程序,并且可以非侵入式地把企业服务应用于POJO。此功能适用于Java SE的编程模型、全部和部分的Java EE。

  作为一个应用程序开发人员,你可以使用Spring平台的各种优势,例如:

  • 使用Java方法执行数据库事务,而不必涉及事务API
  • 使用本地Java方法执行远程过程,而不必涉及远程API
  • 使用本地Java方法执行管理操作,而不必涉及JMX API
  • 使用本地Java方法进行消息处理,而不必涉及JMS API


2.1 依赖注入和控制反转


  背景:“问题是,[它们]反转了控制的哪一方面?”,2004年,Martin Fowler在他个人站点提出了这个关于控制反转(IoC)的问题。Fowler建议重命名这个原则,使得它更好地自我解释,同时提出了依赖注入。要深入了解IoC和DI,可以参考Fowler的文章,地址是:http://martinfowler.com/articles/injection.html

  Java应用程序是一个宽泛的术语----包括从受限的小程序到n层的服务器端企业应用程序---通常由多个对象通过协作而构成整个应用。因此,一个应用程序中的对象彼此存在依赖关系。
  尽管Java平台提供了丰富的应用程序开发功能,它缺少手段来把基本构建块组织成一个有机的整体,只能把这些任务扔给架构师和开发者。当然,你可以使用设计模式,比如Factory,Abstract Factory,Builder,Decorator, 和Service Locator,以组织各种类与对象实例从而拼接出一个应用程序。然而,这些模式仅仅是有一个名称的最佳实践,带有一个这个模式做什么、应用在哪里、解决了什么问题的描述,等等。模式是有效地最佳实践,但你需要在应用程序中自己实现它们。
  Spring框架的控制反转(IoC)组件针对这一问题提供了解决方案,它提供一种有效地手段来把不同的组件组装成一个可完全工作的应用程序。 Spring框架把正式的设计模式作为一等公民“编纂成册”,让你可以集成到自己的应用程序中。许多组织和机构以这种方式使用Spring框架来设计健壮的、可维护的应用程序。


2.2 模块


  Spring框架包含的功能被组织成了大约20个模块。这些模块被划分为Core Container(核心容器)、Data Access/Integration(数据访问/集成)、Web、AOP (Aspect Oriented Programming面向切面编程)、Instrumentation和Test,如下列图所示。

图2.1 Spring框架概述


2.2.1 核心容器


  核心容器包含了核心(Core),Bean组件(Beans),上下文(Context)和表达式语言(Expression Language)模块。
  Core和Bean模块提供框架的基础部分,包括IoC和Dependency Injection功能。BeanFactorys是一个工厂模式的精密实现。它去掉了编程实现单例的需要,并允许你解除配置信息,以及实际程序逻辑的特定依赖之间的耦合。
  Context模块构建在则Core和Bean模块的基础之上:它可以让你以框架中的风格来访问对象,这和JNDI的注册是相似的。上下文模块继承了来自Bean模块的特性,并且添加国际化(比如使用资源包)、事件传播、资源加载和透明创建上下文(如Servlet容器)等方面的支持。上下文模块也支持Java EE特性,比如EJB,JMX和基本的远程调用。ApplicationContext接口是上下文模块的焦点。
  表达式语言模块提供了强大的表达式语言,在运行时查询和操作对象图。这是JSP 2.1规范中的统一表达式语言(Unified EL)的一个扩展。该表达式语言支持设置和获取属性值,属性定义,方法调用,访问数组,集合以及索引的上下文。支持逻辑和数字运算,命名变量。还支持通过名称从Spring的IoC容器中检索对象。它也支持list的投影和选择操作,还有普通的list聚集操作。


2.2.2 数据访问/集成


  数据访问/集成层包括JDBC、ORM、OXM、JMS和事务模块。
  JDBC模拟提供了不需要编写冗长的JDBC代码和解析数据库厂商特有的错误代码的JDBC抽象层。
  ORM模块提供了对流行的对象-关系映射API的集成层,包含JPA,JDO,Hibernate。使用ORM包,你可以使用所有的O/R映射框架并联合Spring提供的所有其他特性,比如前面提到的简单声明式事务管理功能。
  OXM模块集成了支持对象/XML映射实现的抽象层,这些实现包括JAXB、Castor、XMLBeans、JiBX和XStream。
  Java消息服务(JMS模块包含生成和处理消息的特性。
  事务模块支持对实现特定接口的类和所有POJO(普通Java对象)的编程式和声明式的事务管理。


2.2.3 Web


  Web层包含了Web、Web-Servlet、WebSocket和Web-Portlet模块。
  Spring Web模块提供了基本的面向Web的集成功能,例如多个文件上传(multipart file-upload)、使用Servlet监听器和Web应用上下文对IoC容器进行初始化。它也包含Spring远程访问支持的web相关部分。
  Web-Servlet模块包含了Spring对Web应用的模型-视图-控制器(MVC)模式的实现。Spring的MVC框架提供了一个对领域模型代码和Web表单之间的清晰分离,并且集成了其它所有Spring框架的特性。
  Web-Portlet模块提供用于portlet环境和Web-Servlet模块功能镜像的MVC实现。


2.2.4 AOP和Instrumentation基础组件


  Spring AOP模块提供AOP联盟兼容的面向切面编程实现,它允许你自定义,比如,方法拦截器和切入点来完全分离各功能的代码。使用源码级别的元数据功能,你也可以在你的代码中包含行为信息,在某种程度上类似于.NET属性。
  单独的Aspects模块提供了集成使用AspectJ。
  Instrumentation模块提供了类instrumentation的支持,和用于某些应用程序服务器的类加载器实现。


2.2.5 测试Test


  测试模块支持使用JUnit或者TestNG来测试Spring 组件。它提供了对Spring ApplicationContexts和这些上下文的缓存的一致加载。它也提供模拟对象,你可以用它在隔离条件下来测试你的代码。


2.3 应用场景


  前面描述的构建模块使得Spring在很多场景下是一个理想的选择,从小程序到成熟的企业应用程序都可以使用Spring的事务管理功能和web框架集成。

图2.2 典型的完整的Spring Web应用

  Spring 声明式事务管理功能,使得web应用程序可以完全事务化,就好像你使用了EJB容器管理事务一样。所有的自定义业务逻辑都能用简单的POJO实现并使用Spring IoC容器来管理。其他的服务包括对发送邮件和验证的支持,验证是独立于web层的,它可以让你选择在哪里执行验证规则。Spring ORM支持集成JPA,Hibernate和JDO。比如,当使用Hibernate的时候,你可以继续使用已有的映射文件和标准的Hibernate SessionFactory配置。表单控制器无缝地集成了Web层和领域模型,消除了使用ActionForms或者其他类传递HTTP参数的值到领域模型的需要。

图2.3 使用了第三方Web框架的Spring中间层

  有些情况不允许你彻底地切换到一个不同的框架。Spring不强迫你使用它的一切,它不是一个全有或全无的解决方案。现有的前端框架,如Struts、Tapestry、JSF或者那些允许使用Spring事务功能的其他UI框架,都可以与Spring中间层集成你只需要使用ApplicationContext连接你的业务逻辑,并使用WebApplicationContext集成你的web层

图2.4 使用远程调用的应用场景

  当你需要通过web service访问现有代码时,你可以使用Spring的Hessian-Burlap-Rmi-或者JaxRpcProxyFactory类。对现有的应用程序启用远程访问并不难。

图2.5 EJB – 包装已有的POJO

  Spring Framework也提供了对企业级Java Bean(EJB,译者注)的访问和抽象层,这就可以重用已有的POJO并包装它们成无状态会话bean,以应用于可伸缩的、失效安全的Web应用程序中,这些Web应该可能也需要声明式的安全。


2.3.1 依赖管理和命名规范


  依赖关系管理和依赖注入是不同的概念。为了让Spring的这些不错的功能运用到应用程序中(比如依赖注入),你需要收集所有需要的库(JAR文件),并且在编译、运行的时候将它们放到你的类路径classpath中。这些依赖不是注入的虚拟组件,而是一个在文件系统中(通常)的物理资源。依赖管理过程包括定位这些资源,存储它们并添加它们到类路径。依赖关系可以直接的(如我的应用程序运行时依赖于Spring),或者间接的(如我的应用程序依赖commons-dbcp,而commons-dbcp又依赖于commons-pool )。间接的依赖也被称为“传递”,它是最难识别和管理的依赖。
  如果你决定使用Spring,你需要获得那些包含你需要的Spring功能的jar包。为了使这个过程更加简单,Spring被打包为一组模块,这些模块尽可能地分离依赖关系。例如,如果不想写一个web应用程序,你就不需要引入Spring-web模块。为了在本指南中标记Spring库模块,我们使用了速记命名约定spring-* 或者spring-*.jar ,其中*代表模块的短名(比如 spring-core,spring-webmvc, spring-jms等)。实际的jar文件的名字,你是用的通常是模块名字和版本号的联合(如spring-core-4.0.5.RELEASE.jar)
  每个Spring框架的每个发行版将发布组件到下面的位置:
  (1) Maven Central, which is the default repository that Maven queries, and does not require any special configuration to use. Many of the common libraries that Spring depends on also are available from Maven Central and a large section of the Spring community uses Maven for dependency management, so this is convenient for them.The names of the jars here are in the form spring-*-<version>.jar and the Maven groupId is org.springframework.
  (2) In a public Maven repository hosted specifically for Spring. In addition to the final GA releases, this repository also hosts development snapshots and milestones. The jar file names are in the same form as Maven Central, so this is a useful place to get development versions of Spring to use with other libraries deployed in Maven Central. This repository also contains a bundle distribution zip file that contains all Spring jars bundled together for easy download.
  因此,你需要决定的第一件事是如何管理你的依赖关系:我们一般推荐自动化系统,如Maven、Gradle或者Ivy,但是你也可以手动下载所有的jar包。在本章的后面部分我们提供详细的说明。


Spring依赖和依赖于Spring

  虽然Spring为大量的企业和其他的外部工具提供集成和支持,它有意保持其强制性依赖关系到绝对最低:为了对简单的用例使用Spring,你不心去定位和下载(甚至自动地)大量的jar包。基本依赖注入只有一个外部强制性的依赖,即日志logging(见下面对日志选项更详细的描述)。
  接下来我们概述配置一个依赖于Spring的应用程序的基本步骤,首先是Maven然后是Gradle最后是Ivy。在所有的情况下,如果有不清楚的地方,参考依赖关系管理系统的文档,或者看看一些简单的代码。Spring本身使用Gradle管理依赖,当创建的时候,我们的示例通常是使用的Gradle或Maven。


Maven依赖管理


  如果你使用Maven依赖管理你甚至不需要显示地提供日志依赖。例如,创建一个应用程序上下文并使用依赖注入来配置应用程序,你的Maven的依赖将看起来像这样:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.0.5.RELEASE</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>
  很好!注意作用域可以声明为运行时,如果你不需要编译Spring API的话,这通常是使用基本依赖注入的典型案例。
  以上例子应用于Maven中央仓库中。为了使用Spring Maven仓库(例如为里程碑版本或者开发快照版本),你需要在Maven配置中指定一个仓库位置。对于完整发布版本:
<repositories>
    <repository>
        <id>io.spring.repo.maven.release</id>
        <url>http://repo.spring.io/release/</url>
        <snapshots><enabled>false</enabled></snapshots>
    </repository>
</repositories>
  对里程碑:

<repositories>
    <repository>
        <id>io.spring.repo.maven.milestone</id>
        <url>http://repo.spring.io/milestone/</url>
        <snapshots><enabled>false</enabled></snapshots>
    </repository>
</repositories>
  对快照:

<repositories>
    <repository>
        <id>io.spring.repo.maven.snapshot</id>
        <url>http://repo.spring.io/snapshot/</url>
        <snapshots><enabled>true</enabled></snapshots>
    </repository>
</repositories>


Maven的“材料清单”依赖


  使用Maven时有可能意外地混合不同版本的Spring jar。比如,你可能发现第三方库或者其他Spring项目,传递依赖于一个老版本。如果你自己忘记了显示声明一个直接依赖,将会有各种意想不到的问题出现。为了克服这些问题,Maven支持“材料清单”的概念(BOM)的依赖。你可以在dependencyManagement段中导入spring-framework-bom,以确保所有spring依赖项(包括直接依赖和传递依赖)是同一版本。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>4.0.5.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  使用BOM的另一个好处是,当依赖于Spring框架的组件时你不再需要指定<version>属性:
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>
<dependencies>

Gradle依赖管理


  为了通过Gradle构建系统来使用Spring仓库,在repositories部分包括适当的URL:

repositories {
    mavenCentral()
    // and optionally...
    maven { url "http://repo.spring.io/release" }
}
  你可以酌情的改变repositories URL,从/release到/milestone或者/snapshot 。 一旦repository被配置,你可以声明通常的Gradle依赖性:

dependencies {
    compile("org.springframework:spring-context:4.0.5.RELEASE")
    testCompile("org.springframework:spring-test:4.0.5.RELEASE")
}

Ivy依赖管理


  如果你更喜欢用Ivy管理依赖,也有类似的配置选项。
  为了配置Ivy指向Spring仓库,添加以下解析器到你的ivysettings.xml:

<resolvers>
    <ibiblio name="io.spring.repo.maven.release"
            m2compatible="true"
            root="http://repo.spring.io/release/"/>
</resolvers>
  你可以改变root URL,从/release/到/milestone/或者/snapshot/。
  一旦配置完成,你可以以通常的方式添加依赖。比如(in ivy.xml):

<dependency org="org.springframework"
    name="spring-core" rev="4.0.5.RELEASE" conf="compile->runtime"/>


Zip文件的发行包


  虽然使用支持依赖管理的构建系统是获取Spring框架的推荐方式,你仍然是可下载一个完整的zip发行包。
  zips发行包是发布到Spring Maven库(这只是为了我们的便利,在下载这些文件的时候你不需要Maven或者其他的构建系统)。
  为了下载一个Zip发行包,打开一个web浏览器,地址为http://repo.spring.io/release/org/springframework/spring,并选择合适的子目录以定位到你想下载的版本。发行包文以-dist.zip结尾,例如,spring-framework-4.0.5.RELEASE-RELEASE-dist.zip发行包。发行包也发布到milestonessnapshots


2.3.2 日志


  对于Spring,日志是非常重要的依赖,因为:a)它是唯一强制性的外部依赖;b)每个人都喜欢从他们使用的工具上看到一些输出;c)Spring集成的很多其他工具都把日志依赖作为一个选择。应用开发者的一个目标就是通常在一个中心位置为整个应用程序,包括所有外部组件,设置一个统一的日志配置。这会比较难以确定,特别是因为有太多可选择的日志框架。
  Spring中强制的日志文件依赖是Jakarta Commons Logging API(JCL)。我们对Spring的编译是基于JCL的,而且我们使扩展了Spring Framework的类对JCL包的Log对象都是可见的。对于用户来说,所有Spring的版本都使用相同的日志包是非常重要的:因为保留了向后兼容的特性,那么迁移是很容易进行的,即使对扩展自Spring的应用程序也是这样。我们这样做是为了站Spring中的一个模块明确地依赖commons-logging(JCL的典型实现),然后其它的模块在编译时都依赖这个日志包。如果你正在使用Maven的话,同时想知道在哪儿获得commons-logging依赖,那就是来自于Spring,特别其中的spring-core核心模块。
  关于commons-logging的好处是你不需要任何东西就能让你的应用程序程序跑起来。它运行时有一个发现算法,该算法在类路径的所有地方寻找其他的日志框架,并且使用它认为适合的一个(或者你可以告诉它你需要的是哪一个)。如果没有其他可用的日志框架,你可以从JDK(Java.util.logging或者简称JUL)获得logs。在大多数情况下,你可以在控制台查看你的Spring应用程序的工作和日志,这是很重要的。


不使用Commons Logging


  不幸的是,commons-logging的运行时发现算法,虽然方便最终用户,但是有问题的。如果我们能够时光倒流,现在开始一个新的Spring项目并且他使用了不同的日志依赖。第一个选择很可能是Java的简单日志门面Simple Logging Facade(SLF4J),过去也曾被许多其他工具通过Spring使用在他们的应用程序中。基本上有两种方法可以关闭commons-logging:
  (1)从spring-core模块中排除依赖(因为它是唯一的显示依赖于commons-logging的模块)。
  (2)依赖一个特殊的commons-logging依赖,即用空的jar代替实际的库(更多的细节可以在SLF4J FAQ中找到)。
  为了排除commons-logging,添加以下的内容到dependencyMannagement部分:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.0.5.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
  现在,这个应用程序可能被破坏了,因为在类路径上没有实现JCL API,因此要修复它就必须提供有一个新的。在下一节我们将向你展示如何提供一个可选的JCL实现,使用SLF4J作为例子。


使用SLF4J


  SLF4J是一个更加简洁的依赖,并在运行时比commons-logging更加有效。因为它使用编译时绑定,而不是运行时发现来集成其他日志框架。这也意味着,你必须更加明确你想在运行时做什么,并相应地声明它或配置它。SLF4J提供了对很多常见日志框架的绑定,因此你可以选择一个你已经使用的,并且为配置和管理绑定它。
  SLF4J为很多常见日志框架提供了绑定,包括JCL。它也做了相反的事情:在其他日志框架和它自己之间进行桥接。因此在Spring中使用SLF4J时,你需要使用SLF4J-JCL桥接代替commons-logging的依赖。一旦你这么做了,Spring内部的日志调用就会转向调用SLF4J API,因此如果你应用程序中的其他库使用这个API,那么你就需要一个单一的地方来配置和管理日志。
  一个常见的选择就是桥接Spring到SLF4J,提供从SLF4J到Log4J的显示绑定。你需要支持4个的依赖(排除现有的commons-logging):桥接,SLF4J API,到Log4J的绑定,Log4J实现。在Maven中你可以这样做:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.0.5.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.5.8</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.5.8</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.5.8</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.14</version>
    </dependency>
</dependencies>

  这似乎有很多的依赖,仅仅是为了得到一些日志文件。那就这样吧,但是它是可选的,它在类加载器的问题上应该比commons-logging表现的更加的好,这在一个严格的容器如OSGi platform中更显著。据说也有一个性能优势,因为绑定是在编译时而不是运行时。
  SLF4J用户的一个常见的选择是直接绑定Logback,使用它可以用更少的步骤和产生更少的依赖。这消除了多余的绑定步骤,因为Logback直接实现了SLF4J,因此你只需要依赖两个库而不是4个(jcl-over-slf4j和logback)。如果你这样做,你可能还需要从其他外部依赖(不是Spring)排除slf4j-api依赖,因为在类路径中你只需要一个版本的API。


使用Log4J


  许多人使用Log4j作为日志框架,用于配置和管理的目的。它是有效的和完善的,事实上当我们构建和测试Spring时,这也是我们在运行时使用的日志。Spring也提供一些配置和初始化Log4j的工具,因此在某些模块上它有一个可选的编译时Log4j依赖。
  为了使Log4j工作在默认的JCL依赖下(commons-logging),所有你需要做的事就是把Log4j放到类路径下,为它提供配置文件(log4j.properties或者log4j.xml,放在类路径的根目录下)。因此对于Maven用户这就是你的依赖声明:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.14</version>
    </dependency>
</dependencies>
  下面是一个log4j.properties实例,日志打印到控制台:
log4j.rootCategory=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n

log4j.category.org.springframework.beans.factory=DEBUG


运行时容器和本地JCL


  很多的人在提供了JCL实现的容器中运行他们的Spring应用程序。IBM Websphere Application Server (WAS)是一个例子。这样往往会导致问题,遗憾的是没有一个一劳永逸的解决方案;简单的包含commons-logging在大多数情况下是不够的。
 要清楚这一点:报告的问题通常不是JCL本身,或者commons-logging:相反,他们与绑定commons-logging到其他框架(通常是Log4j)有关。这可能会失败,因为commons-logging改变了在运行时环境里,在一些容器中查找老版本(1.0)和新版本(1.1,现在大多数人使用的版本)的方式。Spring不使用JCL API任何不常用的部分,所以没什么破坏,但是一旦Spring或者你的应用程序试图做一些日志记录时,你会发现绑定的Log4j不工作了。
  在这种情况下,WAS最容易的是转化类加载器的层次结构(IBM称之为“parent last”),以便让应用程序控制JCL的依赖关系,而不是容器。该选项不是总是开放的,但是在公共领域也有很多关于可选方安的其他建议,这会因为容器的准确版本和功能集的不同而有所不同。


英文原文:http://docs.spring.io/spring-framework/docs/4.0.5.RELEASE/spring-framework-reference/html/spring-introduction.html

  

  附: 本来想继续翻译Spring框架的其余官方文档,翻译了上面一章下来,发现实在是吃力不讨好的事,翻译过程又慢又耗时间,远没有直接看英文原文来得爽快,所以。。。 还是直接看英文原文 :)

发布了167 篇原创文章 · 获赞 159 · 访问量 195万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览