spring swing_使用Swing的Spring简介

本文档是一份教程,介绍如何使用Spring框架和Swing创建一个简单的待办事项列表应用程序。首先,它概述了Spring和依赖注入的概念,然后详细介绍了设置构建环境(Ant、Maven和Eclipse)的步骤。接着,逐步指导创建Swing组件、Spring的bean定义文件以及应用程序的运行。教程最后简要介绍了Spring Rich Client Project,这是一个用于构建Swing应用程序的框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在你开始前

关于本教程

本教程将帮助您学习使用Swing和依赖项注入 (也称为控制反转(IOC))进行应用程序开发的Spring框架的基础。 在简要概述了Spring和依赖项注入之后,本教程的大部分内容是动手操作的,逐步引导您使用Spring创建功能齐全的Swing应用程序(待办事项列表程序)。 本演练包括用于构建环境的三个选项,每个选项都有详细的设置说明。 您将在此过程中学习Spring框架的基本用法和好处。 一路包括其他相关概念的说明。 您还将进行一些旁听,以在教程代码的上下文中学习一些好的编程实践。

最后一部分是对Spring Rich Client(RCP)框架的简要概述,该框架是Spring框架的子项目,致力于为在Spring下开发富客户端Swing应用程序提供平台。 本教程不包括使用Spring RCP的分步说明,而是一个获取源代码和自行探索项目的起点。

本教程显示了您需要键入的每段新代码。 也提供了完整的源代码(请参阅下载 )。 可下载的源代码是通过直接从本教程开始并粘贴代码而创建的,因此它应无错误,并且与后续操作相同。 因为您将使用Java编程语言编写GUI应用程序,所以它将在运行Java代码的任何平台上运行。

本教程并非旨在全面概述Spring框架或Swing。 它不涉及使用Spring创建Web应用程序或访问数据库,也不涉及诸如Spring对面向方面的编程(AOP)的支持之类的更高级的主题。 探索本教程的相关主题的书籍,文章,教程和在线参考,涵盖春,摆在更大的广度和细节。

先决条件

本教程的读者是Java开发人员。 它旨在提供多种经验水平的访问,即使您熟悉一个主题,您仍可能会学到另一个主题。 一些部分讨论了相关的设计模式和开发方法。 如果您不感兴趣或已经熟悉它们,可以随意浏览。 以下知识和技能水平将有所帮助:

  • 熟悉Java编程语言的基础知识,Java Bean组件的约定以及使用Java语言进行基本应用程序设计和开发的经验。
  • 某些构建环境的基本知识-Eclipse,Apache Ant或Apache Maven(请参阅参考资料 )。 但是,即使您不熟悉这些环境,也可以按照教程中相当详细的说明使用它们。 如果您有一个问题,可以尝试其他两个之一或全部。
  • XML语法的基本知识-元素,属性以及如何维护格式良好的XML文档(请参阅参考资料 )。

您需要一台安装了JDK的计算机(在最后一节中需要JDK 1.5才能运行Rich Client Project演示),并且需要一个Internet连接来下载所需的工具和库。

以下之一是必需的,或者您可以使用自己的构建环境或IDE:

用于下载所需依赖项JAR的选项

为了减少启动本教程所需的前期下载工作量,该教程的源代码中的Ant和Maven构建脚本均提供了对从公共存储库自动下载所需的依赖项JAR(包括Spring本身)的支持。 该功能内置于Maven中,由get-dependencies目标在Ant脚本中提供。 要在Eclipse中使用这些功能,必须安装Ant或Maven。 您可以在“ 环境设置”中找到更多详细信息。

如果愿意,可以手动获得正确的必需JAR版本。 只需参考Ant build.xml文件中的get-dependencies目标(请参见清单1 ),找出需要哪些JAR和版本,以及从中下载它们的URL。

Spring和依赖注入概述

什么是Spring框架?

Spring框架Web站点以以下描述欢迎访问者:“作为领先的全栈Java / J2EE应用程序框架,Spring为许多项目带来了显着的好处,减少了开发工作量和成本,同时提高了测试覆盖率和质量”(请参阅参考资料 )。 该通用语句指示了Spring的范围和大小。 简而言之,Spring提供了许多工具和方法,使编写和测试Java和J2EE应用程序变得更加容易。 本教程仅说明了依赖注入的基本用法,但Spring包含许多其他服务和框架的有用且易于使用的包装器,以及高级功能,例如对面向方面的编程(AOP)的支持(请参阅参考资料 )。

有关更多Spring概述和背景信息,请查看项目的任务书,developerWorks上Spring系列 ,和其他物品的相关主题

什么是依赖注入?

依赖注入(DI),也称为控制反转(IOC),是一种软件开发方法,其中,单独的对象或框架(例如Spring框架)负责创建对象并将其“注入”到其他对象中。依靠他们。 这导致了代码之间的松散耦合,并且易于测试和重用。

请访问Martin Fowler的站点,以获取有关DI / IOC的一些不错的描述(请参阅参考资料 )。

环境设定

选择您的构建环境

在开始对示例应用程序进行编码之前,您需要设置一个环境来构建和运行它。 如果您对Java编程有足够的经验,可以自行完成构建和运行,则可以跳至创建待办事项列表:Basic Swing和Spring应用程序设置

您可以从三个选项中选择一个构建环境(请参阅先决条件 )。 您可以选择一个自己熟悉的人,也可以尝试一个新的人来接触它。 您可以使用任何一种或全部三种:

  • Apache Ant:您可以从命令行(要求您下载并安装Ant)或从IDE(例如Eclipse)中运行Ant构建脚本。
  • Apache Maven:Maven是一种流行的构建环境。 要使用它,必须下载并安装它,但是本教程中包含了使用Maven构建所需的所有必需配置文件。
  • Eclipse:请注意,即使使用Eclipse进行构建和运行,仍然需要获取库JAR依赖项,然后在Eclipse类路径中定义对这些依赖项的引用。 您可以使用Ant或Maven构建脚本将所需的依赖项自动下载到本地计算机,或自己下载它们。 您可以在“ 设置Eclipse”中找到更多详细信息。

设置蚂蚁

如果您选择了Ant作为构建环境:

  1. 安装Ant并确保Ant可执行文件在您的路径上。
  2. 在命令行中键入ant -version以验证是否正确安装了Ant。 或者,您可以从所选的IDE或其他工具中使用Ant。
  3. 在操作系统或IDE中配置Ant之后,创建一个名为todo的目录。 该目录将包含您的所有项目文件。
  4. 在目录的根目录中创建build.xml构建脚本,如清单1所示。

注意:出于演示目的,代码清单中的某些行在您通常不会拆分的地方拆分。 在某些情况下,例如清单1,这将导致您必须修复无效的文件。 在这种情况下,代码清单中会内联包含一个NOTE,指示您删除该便笺,并将各行连接在一起,且不能使用空格。 (Ant看起来足够聪明,可以从URL中删除换行符和空格,但是最好还是使它们看起来不错。)

清单1. build.xml
<project name="todo" default="default">
  <property name="build.dir" location="build"/>
  <property name="lib.dir" location="lib"/>

  <path id="classpath">
    <pathelement location="${build.dir}"/>
    <fileset dir="${lib.dir}"/>
  </path>

  <target name="clean">
    <delete dir="${build.dir}"/>
  </target>
    
  <target name="get-dependencies">
    <mkdir dir="${lib.dir}"/>
    <get dest="${lib.dir}/commons-logging-1.0.3.jar"
       usetimestamp="true" ignoreerrors="true"
       src="//www.ibiblio.org/maven/
NOTE: The URL in the lines above and below this note was split for
readability.  Delete this note and join the URL with no spaces             
       commons-logging/jars/commons-logging-1.0.3.jar"/>
    <get dest="${lib.dir}/spring-1.2.3.jar" 
       usetimestamp="true" ignoreerrors="true" 
       src= "http://www.ibiblio.org/maven/
NOTE: The URL in the lines above and below this note was split for
readability.  Delete this note and join the URL with no spaces        
       springframework/jars/spring-1.2.3.jar"/>
  </target>

  <target name="compile">
    <mkdir dir="${build.dir}/classes"/>
    <javac srcdir="src"
           destdir="${build.dir}/classes"
           classpathref="classpath"
           encoding="UTF8"
           debug="on"
           deprecation="on"
    />
    <copy todir="${build.dir}/classes" overwrite="true">
        <fileset dir="src">
            <include name="**/*.xml"/>
        </fileset>
    </copy>
  </target>

  <target name="run" depends="compile">
    <java classname="todo.ToDo" fork="true">
      <classpath>
        <path refid="classpath"/>
        <pathelement location="${build.dir}/classes"/>
      </classpath>
    </java>
  </target>

  <target name="default" depends="get-dependencies, compile, run"/>
</project>
通过Ant自动下载依赖项

现在,运行get-dependencies通过输入目标, ant get-dependencies在命令行上或者在您的IDE或编辑器运行目标。 您应该看到如下所示的输出:

Buildfile: build.xml

get-dependencies:
    [mkdir] Created dir: D:\projects\todo\lib
      [get] Getting: http://www.ibiblio.org/
          maven/commons-logging/jars/commons-logging-1.0.3.jar
      [get] Getting: http://www.ibiblio.org/maven/
          springframework/jars/spring-1.2.3.jar

BUILD SUCCESSFUL
Total time: 30 seconds

在本教程的后面,将指示您使用Ant脚本中的其他目标。 主要的脚本是cleancompilerun ,其行为与大多数标准Ant脚本一样。

设置Maven

如果您选择使用Maven作为构建环境:

  1. 安装Maven并确保Maven可执行文件在您的路径上。 有关更多详细信息,请参阅Maven站点上的文档(请参阅参考资料 )。
  2. 在命令行中键入maven -v以显示版本并验证Maven是否已正确安装。 您可以通过Eclipse或Maven插件在Eclipse中使用Maven。 有关详细信息,请参见Maven网站。
  3. 在操作系统或IDE中配置Maven之后,创建一个名为todo的目录。 该目录将包含您的所有项目文件。
  4. 在todo目录的根目录中创建project.xml(请参见清单2)和maven.xml(请参见清单3)。
清单2. project.xml
<?xml version="1.0" encoding="UTF-8"?>
<project>
  <pomVersion>3</pomVersion>
  <id>todo</id>
  <name>To Do List</name>
  <currentVersion>1.0</currentVersion>
  <organization>
    <name>To Do List Inc.</name>
    <url>http://localhost/notavailable</url>
    <logo>http://maven.apache.org/images/jakarta-logo-blue.gif</logo>
  </organization>
  <inceptionYear>2005</inceptionYear>
  <package>todo</package>
  <logo>http://maven.apache.org/images/maven.jpg</logo>
  <description>A To Do List</description>
  <shortDescription>A To Do List</shortDescription>
  <url/>
  <issueTrackingUrl/>
  <siteAddress/>
  <siteDirectory/>
  <distributionDirectory>/notavailable/</distributionDirectory>
  <repository>
    <connection/>
    <url/>
  </repository>
  <mailingLists/>
  <developers/>
  <dependencies>
    <dependency>
        <groupId>springframework</groupId>
        <artifactId>spring</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.0.3</version>
    </dependency>
  </dependencies>
  <build>
    <nagEmailAddress/>
    <sourceDirectory>src</sourceDirectory>
    <unitTestSourceDirectory/>
    <unitTest/>
    <resources>
      <resource>
        <directory>src</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
    </resources>
  </build>
</project>
清单3. maven.xml
<project default="test"
    xmlns:m="maven"
    xmlns:ant="jelly:ant">

    <goal name="run">
        <ant:path id="run.classpath" >
            <ant:pathelement path="${maven.test.dest}"/>
            <ant:pathelement path="${maven.build.dest}"/>
            <ant:path refid="maven.dependency.classpath"/>
        </ant:path>
    
        <attainGoal name="java:compile"/>
        <attainGoal name="java:jar-resources"/>

        <ant:java classname="todo.ToDo" fork="true">
            <ant:classpath refid="run.classpath"/>
        </ant:java>    
    </goal>
    
</project>
通过Maven自动下载依赖项

现在,在todo目录的根目录中的命令行中键入maven 。 您看到的输出应如下所示:

__  __
|  \/  |__ _Apache__ ___
| |\/| / _` \ V / -_) ' \  ~ intelligent projects ~
|_|  |_\__,_|\_/\___|_||_|  v. 1.0.2

Attempting to download spring-1.2.3.jar.
1787K downloaded
Attempting to download commons-logging-1.0.3.jar.
30K downloaded
build:start:

java:prepare-filesystem:

java:compile:
    [echo] Compiling to D:\projects\todo/target/classes
    [echo] No java source files to compile.

java:jar-resources:

test:prepare-filesystem:

test:test-resources:

test:compile:
    [echo] No test source files to compile.

test:test:
    [echo] No tests to run.
BUILD SUCCESSFUL
Total time: 35 seconds

不必担心“无测试可运行”消息。 您正在寻找要成功下载的依赖项。 另外,请注意,它们只会下载一次,因此在后续的Maven运行中您不会收到下载消息。

本教程稍后将指导您使用Maven中的特定目标。 主要的对象是cleanjava:compile ,它们是Maven中的标准,而run则是maven.xml中定义的自定义目标。

设置Eclipse

如果您已选择Eclipse作为示例应用程序的构建环境,则本节适用。 您将使用Eclipse创建项目,创建新文件,更改透视图和视图,配置构建路径,等等。 如果你是完全新的Eclipse中,我建议你首先访问Eclipse.org主页来学习让周围并在Eclipse中建设项目的基本知识(见相关主题 )。

首先,转到Java透视图,并在Eclipse工作区中创建一个名为todo的新Java项目。 如果您的工作空间中已经有todo目录(也许您已经为Ant或Maven设置了项目),则即使您可能会收到警告该位置已经存在的目录,也可以使用相同的目录。

创建项目之后,可以打开Navigator视图(在Package Explorer视图中看不到。*项目文件),然后创建/编辑/替换相关的Eclipse配置文件,如清单4至7所示。所有路径相对于todo项目根目录。 通常,您不会直接创建或编辑这些文件(您将使用Eclipse中的菜单选项),但是为了完整起见,此处将其包括在内,以供您参考,如果您对Eclipse项目设置有疑问。

您可以覆盖清单4所示的.project文件,也可以使用创建项目时生成的文件:

清单4. .project
<?xml version"1.0" encoding="UTF-8"?>
<projectDescription>
  <name>todo</name>
  <comment></comment>
  <projects>
  </projects>
  <buildSpec>
    <buildCommand>
      <name>org.eclipse.jdt.core.javabuilder</name>
      <arguments>
      </arguments>
    </buildCommand>
  </buildSpec>
  <natures>
    <nature>org.eclipse.jdt.core.javanature</nature>
  </natures>
</projectDescription>

清单5中显示的.cvsignore文件是可选的,但是如果将项目检入CVS存储库,则需要使用它:

清单5. .cvsignore
target
build
lib
eclipseclasses

如果要通过Ant下载依赖项,或者自己下载依赖项并将它们放在lib子目录中,请使用清单6中的.classpath版本。

清单6.指向lib中Ant下载的依赖项的.classpath
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
  <classpathentry kind="src" path="src"/>
  <classpathentry kind="con" 
     path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
  <classpathentry kind="lib" path="lib/commons-logging-1.0.3.jar"/>
  <classpathentry kind="lib" path="lib/spring-1.2.3.jar"/>
  <classpathentry kind="output" path="eclipseclasses"/>
</classpath>

如果要通过Maven下载依赖项,请使用清单7中的.classpath版本:

清单7.指向MAVEN_REPO中Maven下载的依赖项的.classpath
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
  <classpathentry kind="src" path="src"/>
  <classpathentry kind="con" 
     path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
  <classpathentry kind="var" 
     path= "MAVEN_REPO/commons-logging/jars/commons-logging-1.0.3.jar"/>
  <classpathentry kind="var" 
     path="MAVEN_REPO/springframework/jars/spring-1.2.3.jar"/>
  <classpathentry kind="output" path="eclipseclasses"/>
</classpath>
在Eclipse类路径中下载并定义依赖项JAR

您必须下载适当的依赖关系JAR才能在Eclipse下运行示例应用程序。 为了使此操作更容易,本节中描述的Ant和Maven构建都提供了一种将JAR自动下载到指定位置的方法(请参阅通过Ant 自动下载依赖项和通过Maven自动下载依赖项 )。

要使用Ant自动下载依赖项,请执行以下操作:

  1. 已安装Ant。
  2. 创建build.xml(请参见清单1 )。
  3. 从项目的根目录键入ant get-dependencies

如果您希望使用Maven自动下载依赖项,请执行以下操作:

  1. 安装了Maven。
  2. 创建project.xml(请参见清单2 )。
  3. 从项目的根目录键入ant get-dependencies

如果使用Maven方法,则还需要定义一个名为MAVEN_REPO的Eclipse类路径变量以指定Maven存储库的位置,如图1所示。(默认情况下,它位于用户主目录中。)

图1.设置一个指向MAVEN_REPO的Eclipse类路径变量
设置指向MAVEN_REPO的Eclipse类路径变量

最后,如果您不想使用Ant或Maven,或者由于某种原因自动下载无法正常工作,则可以手动下载所需的依赖项并将它们放在lib文件夹中。 在这种情况下,您将使用清单6中的.classpath示例,该示例引用lib文件夹中的JAR。

另外,如果仍在导航器视图中,请记住切换回Package Explorer视图。 在“程序包资源管理器”视图中使用Java代码更加容易。

创建待办事项清单:Basic Swing和Spring应用程序设置

在本节中,您将创建待办事项列表应用程序的基本可运行框架,包括将在本教程后面部分构建的Swing和Spring文件。 您将执行一个简单的依赖注入示例。 在本节的最后,您将拥有一个正在运行的应用程序。

创建MainFrame,Launcher和ToDo类

要启动Swing应用程序的基础,您需要三个部分:

  • Swing JFrame类的子类。 所有Swing应用程序必须具有一个主外部框架才能包含所有其他组件。 您将调用此类MainFrame
  • 一个Launcher类,负责初始化和配置Spring框架。
  • 具有main方法的类,用于启动应用程序。 您将把这个类命名为ToDo

您可以将这三个单独的类组合为一个或两个类或内部类,但是将它们分开更简单。 在更复杂的应用程序中,将它们分开具有其他优势。 例如,在测试过程中,您可能需要一个专门的Launcher类,可以直接从测试中配置和调用该类-也许是为了避免启动会干扰测试但在正常应用程序启动过程中需要的异步任务。

清单8、9和10分别显示了MainFrameLauncherToDo类的代码。 在项目的根目录中创建一个src目录。 然后在适当的程序包结构中创建MainFrame.java,Launcher.java和ToDo.java。 (这意味着src下的目录结构必须与该类的包名称匹配。)请注意,MainFrame.java在todo.ui子包中,您将在其中保留与用户界面相关的类。

清单8. src / todo / ui / MainFrame.java
package todo.ui;

import java.awt.Dimension;
import java.awt.Frame;
import javax.swing.JFrame;
import javax.swing.WindowConstants;


public class MainFrame extends JFrame {
    public void init() {
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        setSize(new Dimension(600, 400));

        setVisible(true);
        setState(Frame.NORMAL);
        show();
    }
}

MainFrame类是Swing JFrame的非常简单的实现。 用于配置和显示框架的代码是在public void init()方法中定义的。 该方法将是Spring调用的应用程序中的第一个方法,也是Swing应用程序的入口。 您将在下一面板“ 创建Spring app-context.xml bean定义文件”中看到如何调用它。

清单9. src / todo / Launcher.java
package todo;

import 
  org.springframework.context.support.ClassPathXmlApplicationContext;


public class Launcher {
    public void launch() {
        String[] contextPaths = new String[] {"todo/app-context.xml"};
        new ClassPathXmlApplicationContext(contextPaths);
    }
}

Launcher类的目的是通过创建ApplicationContext并向其传递一个包含要在创建Spring app-context.xml bean定义创建的 bean定义文件的路径的数组,来初始化和启动Spring框架。 文件 。 框架启动时,Spring会自动创建MainFrame类,因为该bean将被定义为Singleton(请参阅参考资料 )。 除了ClassPathXmlApplicationContext之外,还有几种其他类型的ApplicationContext实现,但是它们都是为Spring应用程序提供配置的一种方式。

清单10. src / todo / ToDo.java
package todo;

public class ToDo {
    public static void main(String[] args) {
        Launcher launcher = new Launcher();
        launcher.launch();
    }
}

清单10中的ToDo类仅具有一个main方法,该方法创建一个Launcher并在其上调用launch()

键入这些类之后,请确保使用适合您的构建环境的方法来编译它们:

  • Ant:转到包含build.xml的项目的根目录。 在命令行中键入ant compile ,或从您的IDE中调用compile目标。
  • Maven:转到包含maven.xml的项目的根目录。 在命令行中输入maven java:compile
  • Eclipse:选择Build Project ,或确保已自动打开Build

您应该在命令行上收到一条BUILD SUCCESSFUL消息,或者在Eclipse Problems视图中该项目没有错误。 如果有任何问题,请仔细阅读编译器错误消息。 具体来说,请确保您在类路径上具有必需的依赖项JAR。 如果没有它们,请返回“ 环境设置”并阅读如何自动下载所需的依赖项。

注意:从现在开始,您应该在编译所有新文件和更改后立即对其进行编译,并修复出现的任何问题。 为了简洁起见,不会总是明确指示您进行编译。

创建Spring app-context.xml bean定义文件

Spring项目的核心是一个或多个bean定义文件。 这是一个可以具有任何名称的XML文件。 您将调用您的app-context.xml并在todo包中创建它,该包将位于类路径中(参见清单11)。

清单11. src / todo / app-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
          "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="mainFrame" class="todo.ui.MainFrame" init-method="init">
  </bean>
</beans>

bean定义文件的根元素是<beans> ,其中包含<bean>元素。 <bean>元素提供了几个属性,但是对于第一个bean定义,您将仅使用三个:id,class和init-method。 id属性定义了bean的名称,该名称用于从Spring检索它。 class属性告诉Spring创建bean时要实例化哪个类。 init-method属性在类上定义一个方法名称,该名称将在Spring实例化后自动调用。

重要的是要知道默认情况下所有bean都是Singleton,除非您在bean元素上指定了singleton =“ false”属性。 除非指定了lazy-init="true"属性,否则Spring会在首次初始化时自动实例化所有Singleton。

Singletons的这种自动创建是Launcher类仅需要创建ApplicationContext而无需执行其他任何操作的原因。 Spring只是将MainFrame创建为Singleton并在其上调用init()方法,从而使其显示自身。 等不及要尝试了? 转到下一个面板, 运行应用程序 ,以获取有关运行新Spring应用程序的说明。

运行应用程序

要查看您的应用程序,请遵循针对您的构建环境的说明:

  • Ant:转到包含build.xml的项目的根目录。 键入ant run或从您的IDE中调用run目标。
  • Maven:转到包含maven.xml的项目的根目录。 键入maven run
  • Eclipse:将ToDo类作为Java应用程序运行(右键单击ToDo.java并选择Run As > Java Application )。

运行该应用程序时,应该看到一个空白的灰色框架,没有标题,如图2所示。如果您这样做,那么恭喜您-您只在Spring下运行了第一个应用程序!

图2.由Spring创建的Swing JFrame显示的空白屏幕
由Spring创建的Swing JFrame显示的空白屏幕

如果它没有运行,请在控制台上查找任何异常消息。 Spring通常会提供简单明了的描述性错误消息和异常,因此请仔细阅读它们。 即使应用程序成功运行,您也可能会在控制台上看到一些INFO消息,例如“ INFO: Unable to locate MessageSource with name 'messageSource': using default... ” 别担心,这些都是正常的。

注意:从现在开始,“运行应用程序”指令意味着您应该为构建环境运行适当的命令。

定义bean属性

现在您已经有一个由Spring运行的基本bean,可以尝试在app-context.xml中为bean定义一些属性。 您将从定义bean的标题开始。 框架标题的setTitle(String title)访问器方法是在MainFrame类的Frame超类上定义的,因此可以使用它。 将清单12中的新代码添加到app-context.xml中的mainFrame bean的定义中。 记住,您已经创建了src / todo / app-context.xml文件,因此只需要添加新行。 从现在开始,如果代码清单是针对现有文件的,则新(粗体)行将被现有行包围,因此您知道将新行放在何处。

清单12.定义bean的title属性
<bean id="mainFrame" class="todo.ui.MainFrame" init-method="init">
    <property name="title">
      <value>My To Do List</value>
    </property>
  </bean>

运行应用程序,您应该看到My To Do List作为框架的标题,如图3所示:

图3.将标题文本注入JFrame
将标题文本注入JFrame

这是您的依赖注入的第一个简单用法。 您将一个普通的String对象“注入”到MainFrame类的title属性中。 在幕后,Spring会自动创建一个值为My To Do ListString并将其作为参数传递给todo.ui.MainFrame类的setTitle()方法。 这是一个重要的概念,因为Spring中所有其他类型的依赖项注入基本相同。 您可以使用bean定义文件来定义值,对象或对象集合,这些值,对象或对象集合将作为属性传递(注入)到其他对象中。 然后,Spring在运行时通过为您处理对象创建和属性设置将它们连接在一起。

让框架完成将事物连接在一起的工作非常强大。 如果将来需要更改事物连接的方式,则可能只需要更改Spring配置文件,而无需触摸经过全面测试且没有错误的代码。 当然,当您以不同方式将现有组件重新连接在一起时,总会出现集成问题。 但是,如果您的组件是根据良好的面向对象原理(例如,松散耦合和高内聚性)设计的(在下一节“ 创建待办事项列表:制作可重用的组件并在表中显示数据”中对此进行了详细介绍),则您可能会发现这类集成问题相对较少。

创建待办事项清单:制作可重用组件并在表中显示数据

在本节中,您将开始充实应用程序。 在本节的最后,您将看不到列表中的任何数据,但是请耐心等待; 您首先必须创建一些布局管道,以便您有放置数据的位置。 值得等待。 另外,本节将花费一些额外的时间来创建可重用组件,展示如何在不更改Spring的情况下由Spring重用它,并讨论创建可重用组件的好处。

创建一个可重复使用的面板

现在,您将向MainFrame添加一个面板。 我将在这里作弊并预测未来。 我知道您将重用此面板,因此您将使其通用到足以被重用。 这样做将使您在本节的后面(在添加表和重用面板中 )看到,只要在设计时考虑到重用,重用Spring中的现有类是多么容易。 通常,您最初会编写一个更具自定义性和简单性的类,然后使其变得更通用以便以后重用。 (即使那样,也只有在确定需要重用它时才这样做。请参阅“ 相关主题 ”中的“不需要”。)但是,就本教程而言,从一开始就可重复使用,使事情变得简单,并节省时间。

todo.ui软件包中创建一个名为BoxLayoutPanel的类。 BoxLayoutPanel是JPanel的子类,并且具有BoxLayout(请参见清单13):

清单13. src / todo / ui / BoxLayoutPanel.java
package todo.ui;

import java.awt.Component;
import java.util.Iterator;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JPanel;


public class BoxLayoutPanel extends JPanel {
    /**
     * We can't use "components" as the property name,
     * because it conflicts with an existing property
     * on the Component superclass.
     */
    private List panelComponents;
    private int axis;

    public void setAxis(int axis) {
        this.axis = axis;
    }

    public void setPanelComponents(List panelComponents) {
        this.panelComponents = panelComponents;
    }

    public void init() {
        setLayout(new BoxLayout(this, axis));

        for (Iterator iter = panelComponents.iterator();
             iter.hasNext();) {
            Component component = (Component) iter.next();
            add(component);
        }
    }
}

如清单13所示,可使用setAxis()方法设置BoxLayoutaxis(X/Y)属性。 面板上有一个Component List,在初始化时它将自动添加到面板中。 设置BoxLayout并将component s添加到面板的代码是在创建bean时Spring调用的init()方法中,就像MainFrame bean一样。

豆类一起接线

现在,您需要将面板连接到Spring并提供要添加的组件,因此您将如清单14所示修改app-context.xml(通过添加黑体元素):

清单14. src / todo / app-context.xml-将BoxLayoutPanel连接到Spring
<bean id="mainFrame" class="todo.ui.MainFrame" init-method="init">
    <property name="contentPane">
      <ref bean="mainPanel"/>
    </property>
    <property name="title">
      <value>My To Do List</value>
    </property>
  </bean>
  <bean id="mainPanel" class=
     "todo.ui.BoxLayoutPanel" init-method="init">
    <property name="axis">
      <!--  "1" corresponds to BoxLayout.Y_AXIS -->
      <!--  Spring can access constants, but it's more complex -->
      <value>1</value>
    </property>
    <property name="panelComponents">
      <list>
        <ref bean="itemScrollPane"/>
      </list>
    </property>
  </bean>
  <bean id="itemScrollPane" class="javax.swing.JScrollPane">
  </bean>

清单14中有几件事要注意。首先,创建mainPanel bean就像mainFrame bean。 然后,使用contentPane属性和<ref bean="mainPanel"/> 将其注入到mainFrame bean中。 setContentPane()是一种将面板添加到框架的方法。 它可以自动使用,因为MainFrame JFrame子类,因此继承了setContentPane()方法。 <ref bean="mainPanel"/>只是采用Spring创建的mainPanel对象,并将其传递给MainPanelsetContentPane()方法。

您还可以将值注入BoxLayoutPanelaxispanelComponents属性中。 对于axis1对应于BoxLayoutY_AXIS常量。 (请注意,Spring确实提供了一种通过使用FieldRetrievingFactoryBean从静态字段值设置bean属性的方法。但是我不想太花哨,所以您只需对axis常量的值进行硬编码。 )

panelComponents属性具有一个List ,因此您可以使用<list>元素使Spring自动为您创建一个ArrayList 。 Spring可以自动创建以下集合类型: ListSetMapProperties

最后,您定义了第三个bean itemScrollPane ,它是一个JScrollPane,因此List有一个ComponentitemScrollPane不需要任何自定义代码,因此您甚至不需要子类JScrollPane ; 您只需要让Spring直接实例化它即可。 看看如何使用Spring使用现有方法将不了解Spring的现有类的实例连接在一起?

运行该应用程序,您应该看到……几乎和以前一样。 即使添加了PanelScrollPane ,Swing也不显示滚动条,除非有要滚动的内容。 在下一节中,您将向ScrollPane添加一个组件。 忍受-一旦您了解了一些不可避免的无聊之处,这将是一个功能齐全的应用程序。

添加表格并重新使用面板

接下来,将一个名为itemTable的JTable添加到现有的itemScrollPane 。 您还将在现有mainPanel放置另一个BoxLayoutPanel ,名为buttonPanel 。 这为您提供了一个面板来容纳按钮。 这次,您无需编写任何新的Java代码,而只是使用现有类在app-context.xml中定义并连接更多的bean(请参见清单15):

清单15. src / todo / app-context.xml-重用BoxLayoutPanel
<bean id="mainPanel" class="todo.ui.BoxLayoutPanel" 
        init-method="init">
    <property name="axis">
      <!--  "1" corresponds to BoxLayout.Y_AXIS -->
      <!--  Spring can access constants, but it's more complex -->
      <value>1</value>
    </property>
    <property name="panelComponents">
      <list>
        <ref bean="itemScrollPane"/>
        <ref bean="buttonPanel"/>
      </list>
    </property>
  </bean>
  <bean id="itemScrollPane" class="javax.swing.JScrollPane">
    <constructor-arg>
      <ref bean="itemTable"/>
    </constructor-arg>
  </bean>
  <bean id="itemTable" class="javax.swing.JTable">
  </bean>
  <bean id="buttonPanel" class=
     "todo.ui.BoxLayoutPanel" init-method="init">
    <property name="axis">
      <!--  "0" corresponds to BoxLayout.X_AXIS -->
      <value>0</value>
    </property>
    <property name="panelComponents">
      <list>
      </list>
    </property>
  </bean>

还记得您是如何使BoxLayoutPanel通用的,以便可以重用它(请参阅创建可重用的面板 )吗? 就像mainPanel一样, buttonPanelBoxLayoutPanel一个实例。 但是,它保留按钮而不是滚动窗格,并且它将沿X轴而不是Y轴进行布局-但这都是通过Spring进行配置的,因此您无需更改代码。 无需更改代码就可以重用!

花一点时间考虑一下如何使BoxLayoutPanel可重用。 首先,您要小心依赖项。 BoxLayoutPanel接口的唯一要求是axis值和Component List 。 (几乎每个Swing GUI元素都继承自Component 。)您本来可以创建一个ScrollPaneAndButtonPanePanel ,并为每个这些组件具有特定的setScrollPanesetButtonPane设置器,但是这些重用性根本不是很可重用。 相反,您只是获取了Component的通用List并对其进行了迭代(由BoxLayout的线性布局方案简化)。 这意味着可以重复使用此窗格以在垂直或水平线上布置任意数量的GUI组件,并且它永远不需要知道它们是什么类型的组件。 这说明了低耦合和高内聚的良好编程实践(请参阅参考资料 )。 您的BoxLayoutPanel按行或列对组件进行BoxLayoutPanel ,效果很好,并且不做其他任何事情(高内聚性)。 而且,除了它是ComponentList (低耦合)之外,它既不知道也不关心它的布局。

清单15中对app-context.xml的另一个有趣的添加是<constructor-arg>元素的使用。 这是构造函数注入 ,与您到目前为止一直在使用的setter注入相反。 这听起来有点复杂,但实际上并非如此。 您只是通过构造函数参数而不是setter( accessor )方法参数传递( 注入 )依赖项。 在这里使用它的唯一原因是JScrollPane仅允许您通过构造函数添加组件。 您无法通过设置器添加它。

运行该应用程序,您应该会看到-再也没有看到。 别失望-我保证很快就会有更令人兴奋的事情发生。

总而言之,您现在有了MainFrame ,其中包含BoxLayoutPanelBoxLayout面板包含一个带有空JTtableJScrollPane ,以及另一个BoxLayoutPanel用于即将添加的按钮。 However, because these are all container-type components that don't contain anything, they all just show up as a big gray box. But now that all that plumbing is out of the way, you can add some goodies to create something much more exciting.

Defining a table model

In the next section ( Creating the to-do list: Finishing up -- buttons and listeners ), you'll display some items in the to-do list. To accomplish this, you first need to create an implementation of TableModel, which you'll set on your JTable . Your TableModel implementation will be called ItemTableModel , and it will simply be a wrapper for a List of items. To make this easier, you'll extend the AbstractTableModel abstract class provided by Swing and override only the methods you need to in order to make your TableModel behave properly when you add it to the JTable . Listing 16 shows the code for ItemTableModel :

Listing 16. src/todo/ui/ItemTableModel.java
package todo.ui;

import java.util.List;
import javax.swing.table.AbstractTableModel;


public class ItemTableModel extends AbstractTableModel {
    List itemList;

    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return true;
    }

    public int getColumnCount() {
        return 1;
    }

    public String getColumnName(int column) {
        return "Items";
    }

    public void setItemList(List itemList) {
        this.itemList = itemList;
    }

    public int getRowCount() {
        return itemList.size();
    }

    public void setValueAt(Object value, 
                           int rowIndex, int columnIndex) {
        itemList.set(rowIndex, value);
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
        return itemList.get(rowIndex);
    }
}

The methods in ItemTableModel are fairly straightforward and self-explanatory. Cells are always editable. ( isCellEditable() always returns true.) The table has only one column. The row count is always equal to the list size. The setValueAt() and getValueAt() methods simply access the element in the underlying list at the specified row index. The columnIndex parameter can be ignored because the table has only one column. Finally, there are the property and setter for the itemList itself. That's all there is to the class. Save it and make sure it compiles. No need to run the application yet because you haven't yet wired the ItemTableModel class into the rest of the application.

Showing items in the list

You might be wondering where the List of items will come from. That is a very important question, and the very important answer is: You don't care at this point. Right now, the only dependency that your TableModel has on the data is that it implements the List interface. This means that it could come from anywhere -- a database, a network request, or a simple hard-coded ArrayList . (You'll use a simple ArrayList .) It doesn't matter, as long as you put the data into some class that implements the List interface and inject it into your TableModel . This is good for a few reasons:

  • You can finish coding and testing your list-display GUI functionality against a simple hard-coded list, and put off until later the details such how you will store the list or retrieve stored lists.
  • The "real" implementation of your list could be developed by someone else, in a completely different project, with his or her own Spring configuration file, and you could still wire it together with your GUI later to make a single application. Spring lets you have multiple app-context bean definition files.
  • If you do want to add fancy stuff later to your list implementation, such as database persistence (JDBC or ORM) or remote access (Spring supports several different remoting technologies), you can. The Spring framework is filled with lots of helper and wrapper classes for services such as these, as well as examples of how to use them. This can make your development effort much easier (and require much less code) than if you had to use the APIs of the services directly. And the Spring wrappers are designed to be loosely coupled and reusable just like the beans in your to-do list application.
  • Even if you eventually do write a real implementation of the list, you can still plug in a stub implementation at any time it is convenient -- for example, during tests, when you don't want to incur the overhead of a database hit, or when you want to have complete and easy control over the list's behavior (see Related topics for more on test-driven development).

But enough with the gratuitous gushing. 回到代码。 It's time to make the stub list implementation and wire it up with your TableModel and JTable . The define-and-inject pattern for configuring beans in the Spring app-context.xml configuration file should start becoming more familiar by now (see Listing 17):

Listing 17. src/todo/app-context.xml - Adding a stub list implementation
<bean id="itemTable" class="javax.swing.JTable">
    <property name="model">
      <ref bean="itemTableModel"/>
    </property>
  </bean>
  <bean id="itemTableModel" class="todo.ui.ItemTableModel">
    <property name="itemList">
      <ref bean="itemList"/>
    </property>
  </bean>
  <bean id="itemList" class="java.util.ArrayList">
    <constructor-arg>
      <list>
        <value>Item 1</value>
        <value>Item 2</value>
        <value>Item 3</value>
      </list>
    </constructor-arg>
  </bean>

Now, run the application, and you should see your data in the list, as shown in Figure 4:

Figure 4. A list with data in it
Injecting the title text into the JFrame

The list is even editable, because you always return true from isCellEditable . Try it: just click in a cell and type or delete characters. When you get bored with that, move on to the next section, where you'll add buttons for adding and deleting items from the list.

Creating the to-do list: Finishing up with buttons and listeners

You'll finish writing your to-do list application in this section. You'll create buttons and listeners that let you add and delete items from the list. The section also includes some discussion about how your button and listener code is different from most other Swing sample code or tutorials.

Creating buttons and listeners

You're in the home stretch now. All you need to do is create Add New and Delete buttons and hook them up to the list using events and listeners. This is all fairly basic stuff in Swing/Java programming (see Related topics for good examples and tutorials on these topics), so I'll cover it pretty quickly -- especially because the Spring wiring code is pretty similar to what you've already seen. The classes you'll create to make the buttons work use Swing's implementation of the Observer pattern . If you don't understand that pattern you might get a bit confused; see Related topics for more information on the Observer pattern.

First, create a subclass of JButton called ActionListenerButton , which you'll use for both the Add New and Delete buttons (see Listing 18):

Listing 18. src/todo/ui/button/ActionListenerButton.java
package todo.ui.button;

import java.awt.event.ActionListener;
import javax.swing.JButton;


public class ActionListenerButton extends JButton {
    private ActionListener actionListener;

    public void setActionListener(ActionListener actionListener) {
        this.actionListener = actionListener;
    }

    public void init() {
        this.addActionListener(actionListener);
    }
}

The ActionListenerButton class has a property to hold an ActionListener , and an init() method (called by Spring upon bean creation) that adds the ActionListener to the button. This class plays the role of the subject in the Observer pattern, and it will notify its ActionListener when clicked. Notice that ActionListenerButton is in a new todo.ui.button package.

Next, create an abstract superclass called ListTableActionListener , which contains the common functionality of the ActionListeners for your buttons. This common functionality is the existence of properties for the list and table , which the subclasses will access and modify when their actionPerformed() methods are invoked. The subclasses of ListTableActionListener act as the observers in the Observer pattern. Listing 19 shows the code for ListTableActionListener :

Listing 19. src/todo/ui/button/ListTableActionListener.java
package todo.ui.button;

import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JTable;


public abstract class ListTableActionListener 
                      implements ActionListener {
    protected JTable table;
    protected List list;

    public void setList(List list) {
        this.list = list;
    }

    public void setTable(JTable itemTable) {
        this.table = itemTable;
    }
}

Next, create AddNewButtonActionListener , shown in Listing 20:

Listing 20. src/todo/ui/button/AddNewButtonActionListener
package todo.ui.button;

import java.awt.event.ActionEvent;


public class AddNewButtonActionListener extends 
  ListTableActionListener {
    public void actionPerformed(ActionEvent e) {
        list.add("New Item");
        table.revalidate();
    }
}

AddNewButtonActionListener adds a new default item to the list when actionPerformed() is invoked and calls table.revalidate() to make the table display the newly inserted data.

Finally, create DeleteButtonActionListener , shown in Listing 21:

Listing 21. src/todo/ui/button/DeleteButtonActionListener
package todo.ui.button;

import java.awt.event.ActionEvent;


public class DeleteButtonActionListener 
             extends ListTableActionListener {
    public void actionPerformed(ActionEvent e) {
        int selectedRow = table.getSelectedRow();

        if (selectedRow == -1) {
            // if there is no selected row, don't do anything
            return;
        }

        if (table.isEditing()) {
            // if we are editing the table, don't do anything
            return;
        }

        list.remove(selectedRow);
        table.revalidate();
    }
}

This ActionListener gets the index of the currently selected row, if there is one, and removes the corresponding item from the list if the row is not currently being edited. After removing the item, the table is revalidated to display the changes.

Wiring in the buttons and listeners

Now that you have the code for the buttons and ActionListener s, you wire them together with Spring, just like the other beans, in the app-context.xml file (see Listing 22):

Listing 22. src/todo/app-context.xml - Wiring in the buttons and listeners
<bean id="buttonPanel" class="todo.ui.BoxLayoutPanel" init-method="init">
    <property name="axis">
      <!--  "0" corresponds to BoxLayout.X_AXIS -->
      <value>0</value>
    </property>
    <property name="panelComponents">
      <list>
        <ref bean="deleteButton"/>
        <ref bean="addNewButton"/>
      </list>
    </property>
  </bean>
  <bean id="deleteButton" class="todo.ui.button.ActionListenerButton" 
        init-method="init">
    <property name="actionListener">
      <ref bean="deleteButtonActionListener"/>
    </property>
    <property name="text">
      <value>Delete</value>
    </property>
  </bean>
  <bean id="deleteButtonActionListener" 
        class="todo.ui.button.DeleteButtonActionListener">
    <property name="list">
      <ref bean="itemList"/>
    </property>
    <property name="table">
      <ref bean="itemTable"/>
    </property>
  </bean>
  <bean id="addNewButton" class="todo.ui.button.ActionListenerButton" 
        init-method="init">
    <property name="actionListener">
      <ref bean="addNewButtonActionListener"/>
    </property>
    <property name="text">
      <value>Add New</value>
    </property>
  </bean>
  <bean id="addNewButtonActionListener" 
        class="todo.ui.button.AddNewButtonActionListener">
    <property name="list">
      <ref bean="itemList"/>
    </property>
    <property name="table">
      <ref bean="itemTable"/>
    </property>
  </bean>

You now have two button beans, addNewButton and deleteButton , both of which are instances of ActionListenerButton . Two ActionListener beans ( deleteButtonActionListener and addNewButtonActionListener ) are defined. They each have the list and table injected into them and are in turn injected into the buttons. The button beans themselves are added to the buttonPanel bean's list of components, so it will lay them out. Note that both the deleteButtonActionListener and addNewButtonActionListener beans have the same Singleton instances of the list and table beans injected into them. This means that they will both modify the same list and table objects.

而已! Compile and run, and you should see something similar to Figure 5:

Figure 5. The completed To Do List application
The completed To Do List application

Try out the Add New and Delete buttons. Make sure you try adding enough rows and/or resizing the window so that you can see the scrollbar working. You now have a fully functional, rich-client application, written using Spring and Swing. There are a few bugs hiding in it, resulting from this simple implementation. You get extra credit if you find them, and double extra credit if you fix them. If you want to see a much more production-quality example of a Swing application written using Spring, continue on to the next section and check out the Spring Rich Client Project.

The Spring Rich Client Project

This section introduces you to the Spring Rich Client Project (RCP). Although it is still in prerelease development at the time this tutorial is being written, it is a very interesting example of Spring being used to build sophisticated Swing applications.

A slightly higher skill level is required for this section because you'll need to obtain and run (and possibly build) the project yourself. Currently, JDK 1.5 is required to run the sample application. Also, be aware that latest source might have some temporary problems building or running because it is in still active development.

Spring Rich Client Project overview

The main Spring framework project has a subproject called The Spring Rich Client Project. It is hosted as a SourceForge project and has an active mailing list (see Related topics ). Although this project is still in a prerelease stage and undergoing architectural changes, it is functional and usable.

The Spring RCP is a good example of the Spring framework's flexibility and of the way Spring provides building blocks that can be used as a foundation for more complex applications and frameworks. It is a basic framework for building Swing-based GUI applications. By providing high-level abstractions such as commands, lifecycle, rules, wizards, forms, views, and preferences, it lets you create nice-looking GUI components and event handling without too much low-level code. A "Petclinic" sample application bundled with the RCP serves as a good example.

At the time this tutorial is being published, no distributed release package of Spring RCP is available. Recent copies of the RCP binaries and source are available at a hosting site (see Related topics ). If you can't get it there, or you want the latest source, you need to check it out from the Spring RCP CVS repository and build it.

To get the RCP source from CVS, visit the CVS page for the RCP on SourceForge, and check out the HEAD (see Related topics ). If you don't know how to use CVS, you can visit the CVS home page and/or download a GUI CVS client such as TortoiseCVS or Cervisia (see Related topics ). If you use Eclipse, then you can use the CVS Repository Exploring perspective to check out the code.

Once you have the code checked out, find the Ant script at samples/petclinic/build.xml and run the build-standalone target. Then run the commands in samples/petclinic/bin/petclinic-standalone.bat. If you have problems and you checked out the source, you might need to rebuild using the clean and alljars targets in the main build.xml in project's root. If you use Eclipse, run samples/petclinic/src/ -> org.springframework.richclient.samples.petclinic.PetClinicStandalone .

When you run the application, you should get a wizard and then a login screen. Enter any ID and password and click on Cancel to get into the application, as shown in Figure 6:

Figure 6. The Spring Rich Client Project Petclinic sample application
The Spring Rich Client Project Petclinic sample application

Play around with the application a bit and then look into the Petclinic sample source code to see how it works. Even if you don't use the RCP directly, the code can provide you with ideas for designing your own rich-client GUIs using Spring. The RCP and the bundled sample applications also provide good examples of how to use the services provided by the core Spring framework, including event handling, remoting with Hessian,JDBC datasource access, and transaction management with AOP. Just as in your to-do list application, the XML context files are a good starting place to learn how a Spring application is put together.

摘要

In this tutorial, you created a Swing GUI application from scratch, using the Spring framework. You learned about the use and benefits of Spring and about dependency injection. And you learned a bit about good programming practices, such as creating reusable components, and the benefits of loose coupling and high cohesion. You also took a look at the Spring Rich Client Project, which is an example of a framework to support the construction of Swing-based GUI applications using Spring. To learn more, try using Spring out on your next or current project. As you can see from this tutorial, it's easy to learn and use the basics.

Also, using Spring is noninvasive, so you could easily use it in a new module of an existing application without affecting the rest of the application. If you can create an ApplicationContext and get beans from it, then you can use Spring to wire your objects together. Even if you don't care about using dependency injection for your application right now, you can still use the many useful components that are provided as part of the Spring framework.

You don't even need to use an ApplicationContext if you don't want to. Instead, you can use the helper and wrapper classes from the framework directly, instantiating and wiring them together manually (although this would probably be harder than just configuring and using them through an ApplicationContext ). The point is that you could if you wanted to, because of the flexibility and reusability that are designed into all aspects of Spring.

In my experience, using dependency injection is a very different approach to designing applications. It simplifies many things, and it can cause you to come up with new and different designs that are better than you would have otherwise. It is especially powerful if you use unit testing a lot, because it makes it much easier to write loosely coupled, easily testable units of code. Some of the benefits are subtle and take a while to realize, such as the reduced maintenance required because there is less configuration and wiring code to maintain. Don't take my word for it though -- try it out on a project of your own, and please feel free to let me know how it goes. Thanks for reading this tutorial, and remember that all feedback is welcome!


翻译自: https://www.ibm.com/developerworks/java/tutorials/j-springswing/j-springswing.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值