依赖注入和富客户机
创建 to-do 列表:基本的 Swing 和 Spring 应用程序设置
在本节中,我们将创建一个 to-do 列表应用程序的基本运行骨架,包括在本文稍后编译的 Swing 和 Spring 文件。我们将实现一个依赖注入的简单例子。在本节最后,就可以获得一个可运行的程序。
创建 MainFrame、Launcher 和 ToDo 类
要使用 Swing 应用程序的基本框架,需要以下三部分内容:
-
一个继承 Swing
JFrame
类的子类。所有的 Swing 应用程序都有一个主要的外部框架来包含所有其他组件。我们称这个类为MainFrame
。 -
一个
Launcher
类,负责对 Swing 框架进行初始化和配置。 -
一个具有
main
方法的类,用来启动应用程序。我们称这个类为ToDo
。
可以将这三个单独的类组合为一个或二个类,但是将它们作为单独的类实现更简单。将它们作为单独的类在较复杂的应用程序中可以发挥更大的优势。例如,在测试过程中,可能会希望使用一个专用的 Launcher
类,可以从测试中对其进行配置或直接调用 —— 这可以避免普通应用程序启动任务与测试任务之间的不同步,从而不妨碍测试。
清单 8、9、10 分别给出了 MainFrame
、Launcher
和 ToDo
类的代码。请在该项目的根目录中创建一个 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 类的目的是初始化并启动 Spring 框架,这是通过创建一个 ApplicationContext 并向其传递一个包含到 bean 定义文件(这个文件是在 创建
Spring app-context.xml bean 定义文件 中创建的)的路径的数组实现的。在这个框架启动时,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 Automatically。
在命令行中应该会看到一条 BUILD SUCCESSFUL
消息,或者在 Eclipse Problems 视图中没有有关该项目的错误消息。如果遇到任何问题,请仔细阅读编译错误消息。特别是要确保在 classpath 中有所需要的依赖 JAR 文件。如果还没有这些文件,请返回环境设置 一节,并了解如何自动下载所需要的依赖文件。
注: 从现在开始,将会编译所有的新文件,如果碰到任何错误,请立即修改并重新编译。为了简单起见,我们就不再明确地说如何编译了。
创建 Spring app-context.xml bean 定义文件
Spring 项目的核心是 bean 定义 文件。这是一个 XML 文件,名字可以任意。我们将这个文件命名为 app-context.xml,并将其放到 todo
包中,这也在 classpath 中(请参阅清单 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" 属性。Spring 会在第一次对其进行初始化时自动对所有的 Singleton 进行实例化,除非指定了 lazy-init="true"
属性。
这种自动创建 Singleton 的特性就是 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 通常会提供简单的描述性错误信息和异常信息,因此要仔细阅读。即使应用程序成功运行了,也可能会看到一些 INFO
消息显示在控制台上,例如 “INFO: Unable to locate MessageSource with name 'messageSource': using
default...
”。不用担心,这都是正常的。
注: 从现在开始,“运行应用程序”就意味着应该运行适合自己编译环境的命令。
现在已经有了一个使用 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 的标题属性
<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 添加标题文本

这是对依赖注入性技术的第一次简单应用。您将一个普通的 String
对象插入了 MainFrame
类的 title
属性中。在幕后,Spring 会自动创建一个 String
,其值为 My
To Do List
,并将其作为一个参数传递给 todo.ui.MainFrame
类的 setTitle()
方法。这是一个非常重要的概念,由于 Spring 中其他类型的依赖注入基本上都是相同的。可以使用 bean 定义文件来定义值、对象或属性集合,它们可以作为属性传递(注入)到其他对象中。然后,Spring
会在运行时综合应用这些内容,处理对象的创建和属性的设置。
使用这个框架来实现配置其他内容的功能十分强大。如果将来需要修改配置内容的方法,只需要修改 Spring 的配置文件即可,并不需要修改已经经过详细测试的没什么 bug 的代码。当然,如果使用不同的方法来重新编写现有的组件,通常就会碰到集成的问题。然而,如果组件是根据良好的面向对象原则进行设计的,例如松耦合和高度聚合(有关这个主题的更多内容请参阅下一节 创建 to-do 列表:创建一个可重用组件并在表中显示数据),那么就可能会发现这些集成问题并不多。