Introducing the Spring Framework --1.(an article posted on TheServerSide.com)

Since the first version of this article was published in October, 2003, the Spring Framework has steadily grown in popularity. It has progressed through version 1.0 final to the present 1.2, and has been adopted in a wide range of industries and projects. In this article, I'll try to explain what Spring sets out to achieve, and how I believe it can help you to develop J2EE applications.

Yet another framework?

You may be thinking "not another framework." Why should you read this article, or download the Spring Framework (if you haven't already), when there are so many J2EE frameworks, or when you could build your own framework? The sustained high level of interest in the community is one indication that Spring must offer something valuable; there are also numerous technical reasons.

I believe that Spring is unique, for several reasons:

  • It addresses important areas that many other popular frameworks don't. Spring focuses around providing a way to manage your business objects.
  • Spring is both comprehensive and modular. Spring has a layered architecture, meaning that you can choose to use just about any part of it in isolation, yet its architecture is internally consistent. So you get maximum value from your learning curve. You might choose to use Spring only to simplify use of JDBC, for example, or you might choose to use Spring to manage all your business objects. And it's easy to introduce Spring incrementally into existing projects.
  • Spring is designed from the ground up to help you write code that's easy to test. Spring is an ideal framework for test driven projects.
  • Spring is an increasingly important integration technology, its role recognized by several large vendors.

Spring is not necessarily one more framework dependency for your project. Spring is potentially a one-stop shop, addressing most infrastructure concerns of typical applications. It also goes places other frameworks don't.

An open source project since February 2003, Spring has a long heritage. The open source project started from infrastructure code published with my book, Expert One-on-One J2EE Design and Development, in late 2002. Expert One-on-One J2EE laid out the basic architectural thinking behind Spring. However, the architectural concepts go back to early 2000, and reflect my experience in developing infrastructure for a series of successful commercial projects.

Since January 2003, Spring has been hosted on SourceForge. There are now 20 developers, with the leading contributors devoted full-time to Spring development and support. The flourishing open source community has helped it evolve into far more than could have been achieved by any individual.

Architectural benefits of Spring

Before we get down to specifics, let's look at some of the benefits Spring can bring to a project:

  • Spring can effectively organize your middle tier objects, whether or not you choose to use EJB. Spring takes care of plumbing that would be left up to you if you use only Struts or other frameworks geared to particular J2EE APIs. And while it is perhaps most valuable in the middle tier, Spring's configuration management services can be used in any architectural layer, in whatever runtime environment.
  • Spring can eliminate the proliferation of Singletons seen on many projects. In my experience, this is a major problem, reducing testability and object orientation.
  • Spring can eliminate the need to use a variety of custom properties file formats, by handling configuration in a consistent way throughout applications and projects. Ever wondered what magic property keys or system properties a particular class looks for, and had to read the Javadoc or even source code? With Spring you simply look at the class's JavaBean properties or constructor arguments. The use of Inversion of Control and Dependency Injection (discussed below) helps achieve this simplification.
  • Spring can facilitate good programming practice by reducing the cost of programming to interfaces, rather than classes, almost to zero.
  • Spring is designed so that applications built with it depend on as few of its APIs as possible. Most business objects in Spring applications have no dependency on Spring.
  • Applications built using Spring are very easy to unit test.
  • Spring can make the use of EJB an implementation choice, rather than the determinant of application architecture. You can choose to implement business interfaces as POJOs or local EJBs without affecting calling code.
  • Spring helps you solve many problems without using EJB. Spring can provide an alternative to EJB that's appropriate for many applications. For example, Spring can use AOP to deliver declarative transaction management without using an EJB container; even without a JTA implementation, if you only need to work with a single database.
  • Spring provides a consistent framework for data access, whether using JDBC or an O/R mapping product such as TopLink, Hibernate or a JDO implementation.
  • Spring provides a consistent, simple programming model in many areas, making it an ideal architectural "glue." You can see this consistency in the Spring approach to JDBC, JMS, JavaMail, JNDI and many other important APIs.

Spring is essentially a technology dedicated to enabling you to build applications using POJOs. This desirable goal requires a sophisticated framework, which conceals much complexity from the developer.

Thus Spring really can enable you to implement the simplest possible solution to your problems. And that's worth a lot.

What does Spring do?

Spring provides a lot of functionality, so I'll quickly review each major area in turn.

Mission statement

Firstly, let's be clear on Spring's scope. Although Spring covers a lot of ground, we have a clear vision as to what it should and shouldn't address.

Spring's main aim is to make J2EE easier to use and promote good programming practice. It does this by enabling a POJO-based programming model that is applicable in a wide range of environments.

Spring does not reinvent the wheel. Thus you'll find no logging packages in Spring, no connection pools, no distributed transaction coordinator. All these things are provided by open source projects (such as Commons Logging, which we use for all our log output, or Commons DBCP), or by your application server. For the same reason, we don't provide an O/R mapping layer. There are good solutions to this problem such as TopLink, Hibernate and JDO.

Spring does aim to make existing technologies easier to use. For example, although we are not in the business of low-level transaction coordination, we do provide an abstraction layer over JTA or any other transaction strategy.

Spring doesn't directly compete with other open source projects unless we feel we can provide something new. For example, like many developers, we have never been happy with Struts, and felt that there was room for improvement in MVC web frameworks. (With Spring MVC adoption growing rapidly, it seems that many agree with us.) In some areas, such as its lightweight IoC container and AOP framework, Spring does have direct competition, but Spring was a pioneer in those areas.

Spring benefits from internal consistency. All the developers are singing from the same hymn sheet, the fundamental ideas remaining faithful to those of Expert One-on-One J2EE Design and Development. And we've been able to use some central concepts, such as Inversion of Control, across multiple areas.

Spring is portable between application servers. Of course ensuring portability is always a challenge, but we avoid anything platform-specific or non-standard in the developer's view, and support users on WebLogic, Tomcat, Resin, JBoss, Jetty, Geronimo, WebSphere and other application servers. Spring's non-invasive, POJO, approach enables us to take advantage of environment-specific features without sacrificing portability, as in the case of enhanced WebLogic transaction management functionality in Spring 1.2 that uses BEA proprietary APIs under the covers.

Inversion of control container

The core of Spring is the org.springframework.beans package, designed for working with JavaBeans. This package typically isn't used directly by users, but underpins much Spring functionality.

The next higher layer of abstraction is the bean factory. A Spring bean factory is a generic factory that enables objects to be retrieved by name, and which can manage relationships between objects.

Bean factories support two modes of object:

  • Singleton: in this case, there's one shared instance of the object with a particular name, which will be retrieved on lookup. This is the default, and most often used, mode. It's ideal for stateless service objects.
  • Prototype or non-singleton: in this case, each retrieval will result in the creation of an independent object. For example, this could be used to allow each caller to have its own distinct object reference.

Because the Spring container manages relationships between objects, it can add value where necessary through services such as transparent pooling for managed POJOs, and support for hot swapping, where the container introduces a level of indirection that allows the target of a reference to be swapped at runtime without affecting callers and without loss of thread safety. One of the beauties of Dependency Injection (discussed shortly) is that all this is possible transparently, with no API involved.

As org.springframework.beans.factory.BeanFactory is a simple interface, it can be implemented in different ways. The BeanDefinitionReader interface separates the metadata format from BeanFactory implementations themselves, so the generic BeanFactory implementations Spring provides can be used with different types of metadata. You could easily implement your own BeanFactory or BeanDefinitionReader, although few users find a need to. The most commonly used BeanFactory definitions are:

  • XmlBeanFactory. This parses a simple, intuitive XML structure defining the classes and properties of named objects. We provide a DTD to make authoring easier.
  • DefaultListableBeanFactory: This provides the ability to parse bean definitions in properties files, and create BeanFactories programmatically.

Each bean definition can be a POJO (defined by class name and JavaBean initialisation properties or constructor arguments), or a FactoryBean. The FactoryBean interface adds a level of indirection. Typically this is used to create proxied objects using AOP or other approaches: for example, proxies that add declarative transaction management. This is conceptually similar to EJB interception, but works out much simpler in practice, and is more powerful.

BeanFactories can optionally participate in a hierarchy, "inheriting" definitions from their ancestors. This enables the sharing of common configuration across a whole application, while individual resources such as controller servlets also have their own independent set of objects.

This motivation for the use of JavaBeans is described in Chapter 4 of Expert One-on-One J2EE Design and Development, which is available on the ServerSide as a free PDF (/articles/article.tss?l=RodJohnsonInterview).

Through its bean factory concept, Spring is an Inversion of Control container. (I don't much like the term container, as it conjures up visions of heavyweight containers such as EJB containers. A Spring BeanFactory is a container that can be created in a single line of code, and requires no special deployment steps.) Spring is most closely identified with a flavor of Inversion of Control known as Dependency Injection--a name coined by Martin Fowler, Rod Johnson and the PicoContainer team in late 2003.

The concept behind Inversion of Control is often expressed in the Hollywood Principle: "Don't call me, I'll call you." IoC moves the responsibility for making things happen into the framework, and away from application code. Whereas your code calls a traditional class library, an IoC framework calls your code. Lifecycle callbacks in many APIs, such as the setSessionContext() method for session EJBs, demonstrate this approach.

Dependency Injection is a form of IoC that removes explicit dependence on container APIs; ordinary Java methods are used to inject dependencies such as collaborating objects or configuration values into application object instances. Where configuration is concerned this means that while in traditional container architectures such as EJB, a component might call the container to say "where's object X, which I need to do my work", with Dependency Injection the container figures out that the component needs an X object, and provides it to it at runtime. The container does this figuring out based on method signatures (usually JavaBean properties or constructors) and, possibly, configuration data such as XML.

The two major flavors of Dependency Injection are Setter Injection (injection via JavaBean setters); and Constructor Injection (injection via constructor arguments). Spring provides sophisticated support for both, and even allows you to mix the two when configuring the one object.

As well as supporting all forms of Dependency Injection, Spring also provides a range of callback events, and an API for traditional lookup where necessary. However, we recommend a pure Dependency Injection approach in general.

Dependency Injection has several important benefits. For example:

  • Because components don't need to look up collaborators at runtime, they're much simpler to write and maintain. In Spring's version of IoC, components express their dependency on other components via exposing JavaBean setter methods or through constructor arguments. The EJB equivalent would be a JNDI lookup, which requires the developer to write code that makes environmental assumptions.
  • For the same reasons, application code is much easier to test. For example, JavaBean properties are simple, core Java and easy to test: simply write a self-contained JUnit test method that creates the object and sets the relevant properties.
  • A good IoC implementation preserves strong typing. If you need to use a generic factory to look up collaborators, you have to cast the results to the desired type. This isn't a major problem, but it is inelegant. With IoC you express strongly typed dependencies in your code and the framework is responsible for type casts. This means that type mismatches will be raised as errors when the framework configures the application; you don't have to worry about class cast exceptions in your code.
  • Dependencies are explicit. For example, if an application class tries to load a properties file or connect to a database on instantiation, the environmental assumptions may not be obvious without reading the code (complicating testing and reducing deployment flexibility). With a Dependency Injection approach, dependencies are explicit, and evident in constructor or JavaBean properties.
  • Most business objects don't depend on IoC container APIs. This makes it easy to use legacy code, and easy to use objects either inside or outside the IoC container. For example, Spring users often configure the Jakarta Commons DBCP DataSource as a Spring bean: there's no need to write any custom code to do this. We say that an IoC container isn't invasive: using it won't invade your code with dependency on its APIs. Almost any POJO can become a component in a Spring bean factory. Existing JavaBeans or objects with multi-argument constructors work particularly well, but Spring also provides unique support for instantiating objects from static factory methods or even methods on other objects managed by the IoC container.

This last point deserves emphasis. Dependency Injection is unlike traditional container architectures, such as EJB, in this minimization of dependency of application code on container. This means that your business objects can potentially be run in different Dependency Injection frameworks - or outside any framework - without code changes.

In my experience and that of Spring users, it's hard to overemphasize the benefits that IoC--and, especially, Dependency Injection--brings to application code.

Dependency Injection is not a new concept, although it's only recently made prime time in the J2EE community. There are alternative DI containers: notably, PicoContainer and HiveMind. PicoContainer is particularly lightweight and emphasizes the expression of dependencies through constructors rather than JavaBean properties. It does not use metadata outside Java code, which limits its functionality in comparison with Spring. HiveMind is conceptually more similar to Spring (also aiming at more than just IoC), although it lacks the comprehensive scope of the Spring project or the same scale of user community. EJB 3.0 will provide a basic DI capability as well.

Spring BeanFactories are very lightweight. Users have successfully used them inside applets, as well as standalone Swing applications. (They also work fine within an EJB container.) There are no special deployment steps and no detectable startup time associated with the container itself (although certain objects configured by the container may of course take time to initialize). This ability to instantiate a container almost instantly in any tier of an application can be very valuable.

The Spring BeanFactory concept is used throughout Spring, and is a key reason that Spring is so internally consistent. Spring is also unique among IoC containers in that it uses IoC as a basic concept throughout a full-featured framework.

Most importantly for application developers, one or more BeanFactories provide a well-defined layer of business objects. This is analogous to, but much simpler (yet more powerful), than a layer of local session beans. Unlike EJBs, the objects in this layer can be interrelated, and their relationships managed by the owning factory. Having a well-defined layer of business objects is very important to a successful architecture.

A Spring ApplicationContext is a subinterface of BeanFactory, which provides support for:

  • Message lookup, supporting internationalization
  • An eventing mechanism, allowing application objects to publish and optionally register to be notified of events
  • Automatic recognition of special application-specific or generic bean definitions that customize container behavior
  • Portable file and resource access

XmlBeanFactory example

Spring users normally configure their applications in XML "bean definition" files. The root of a Spring XML bean definition document is a <beans> element. The <beans> element contains one or more <bean> definitions. We normally specify the class and properties of each bean definition. We must also specify the id, which will be the name that we'll use this bean with in our code.

Let's look at a simple example, which configures three application objects with relationships commonly seen in J2EE applications:

  • A J2EE DataSource
  • A DAO that uses the DataSource
  • A business object that uses the DAO in the course of its work

In the following example, we use a BasicDataSource from the Jakarta Commons DBCP project. (ComboPooledDataSource from the C3PO project is also an excellent option.) BasicDataSource, like many other existing classes, can easily be used in a Spring bean factory, as it offers JavaBean-style configuration. The close method that needs to be called on shutdown can be registered via Spring's "destroy-method" attribute, to avoid the need for BasicDataSource to implement any Spring interface.

<beans>

  <bean id="myDataSource"
	class="org.apache.commons.dbcp.BasicDataSource"
	destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/mydb" />
    <property name="username" value="someone" />
  </bean>

All the properties of BasicDataSource we're interested in are Strings, so we specify their values with the "value" attribute. (This shortcut was introduced in Spring 1.2. It's a convenient alternative to the <value> subelement, which is usable even for values that are problematic in XML attributes.) Spring uses the standard JavaBean PropertyEditor mechanism to convert String representations to other types if necessary.

Now we define the DAO, which has a bean reference to the DataSource. Relationships between beans are specified using the "ref" attribute or <ref> element:

  <bean id="exampleDataAccessObject"
		class="example.ExampleDataAccessObject">
    <property name="dataSource" ref="myDataSource" />
  </bean>

The business object has a reference to the DAO, and an int property (exampleParam). In this case, I've used the subelement syntax familiar to those who've used Spring prior to 1.2:

<bean id="exampleBusinessObject"
		class="example.ExampleBusinessObject">
    <property name="dataAccessObject"><ref bean="exampleDataAccessObject"/></property>
    <property name="exampleParam"><value>10</value></property>
  </bean>

</beans>

Relationships between objects are normally set explicitly in configuration, as in this example. We consider this to be a Good Thing in most cases. However, Spring also provides what we call "autowire" support, where it figures out the dependencies between beans. The limitation with this - as with PicoContainer - is that if there are multiple beans of a particular type it's impossible to work out which instance a dependency of that type should be resolved to. On the positive side, unsatisfied dependencies can be caught when the factory is initialized. (Spring also offers an optional dependency check for explicit configuration, which can achieve this goal.)

We could use the autowire feature as follows in the above example, if we didn't want to code these relationships explicitly:

<bean id="exampleBusinessObject"
	class="example.ExampleBusinessObject"
	autowire="byType">

    <property name="exampleParam" value="10" />
 </bean>

With this usage, Spring will work out that the dataSource property of exampleBusinessObject should be set to the implementation of DataSource it finds in the present BeanFactory. It's an error if there is none, or more than one, bean of the required type in the present BeanFactory. We still need to set the exampleParam property, as it's not a reference.

Autowire support has the advantage of reducing the volume of configuration. It also means that the container can learn about application structure using reflection, so if you add an additional constructor argument of JavaBean property, it may be successfully populated without any need to change configuration. The tradeoffs around autowiring need to be evaluated carefully.

Externalizing relationships from Java code has an enormous benefit over hard coding it, as it's possible to change the XML file without changing a line of Java code. For example, we could simply change the myDataSource bean definition to refer to a different bean class to use an alternative connection pool, or a test data source. We could use Spring's JNDI location FactoryBean to get a datasource from an application server in a single alternative XML stanza, as follows. There would be no impact on Java code or any other bean definitions.

<bean id="myDataSource"
	class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/myDataSource" />
  </bean>

Now let's look at the Java code for the example business object. Note that there are no Spring dependencies in the code listing below. Unlike an EJB container, a Spring BeanFactory is not invasive: you don't normally need to code awareness of it into application objects.

public class ExampleBusinessObject implements MyBusinessObject {

	private ExampleDataAccessObject dao;
	private int exampleParam;

	public void setDataAccessObject(ExampleDataAccessObject dao) {
		this.dao = dao;
	}

	public void setExampleParam(int exampleParam) {
		this.exampleParam = exampleParam;
	}

	public void myBusinessMethod() {
		// do stuff using dao
	}
}

Note the property setters, which correspond to the XML references in the bean definition document. These are invoked by Spring before the object is used.

Such application beans do not need to depend on Spring: They don't need to implement any Spring interfaces or extend Spring classes: they just need to observe JavaBeans naming convention. Reusing one outside of a Spring application context is easy, for example in a test environment. Just instantiate it with its default constructor, and set its properties manually, via setDataSource() and setExampleParam() calls. So long as you have a no-args constructor, you're free to define other constructors taking multiple properties if you want to support programmatic construction in a single line of code.

Note that the JavaBean properties are not declared on the business interface callers will work with. They're an implementation detail. We can easily "plug in" different implementing classes that have different bean properties without affecting connected objects or calling code.

Of course Spring XML bean factories have many more capabilities than described here, but this should give you a feel for the basic approach. As well as simple properties, and properties for which you have a JavaBeans PropertyEditor, Spring can handle lists, maps and java.util.Properties. Other advanced container capabilities include:

  • Inner beans, in which a property element contains an anonymous bean definition not visible at top-level scope
  • Post processors: special bean definitions that customize container behavior
  • Method Injection, a form of IoC in which the container implements an abstract method or overrides a concrete method to inject a dependency. This is a more rarely used form of Dependency Injection than Setter or Constructor Injection. However, it can be useful to avoid an explicit container dependency when looking up a new object instance for each invocation, or to allow configuration to vary over time--for example, with the method implementation being backed by a SQL query in one environment and a fil system read in another.

Bean factories and application contexts are often associated with a scope defined by the J2EE server or web container, such as:

  • The Servlet context. In the Spring MVC framework, an application context is defined for each web application containing common objects. Spring provides the ability to instantiate such a context through a listener or servlet without dependence on the Spring MVC framework, so it can also be used in Struts, WebWork or other web frameworks.
  • A Servlet: Each controller servlet in the Spring MVC framework has its own application context, derived from the root (application-wide) application context. It's also easy to accomplish this with Struts or another MVC framework.
  • EJB: Spring provides convenience superclasses for EJB that simplify EJB authoring and provide a BeanFactory loaded from an XML document in the EJB Jar file.

These hooks provided by the J2EE specification generally avoid the need to use a Singleton to bootstrap a bean factory.

However, it's trivial to instantiate a BeanFactory programmatically if we wish. For example, we could create the bean factory and get a reference to the business object defined above in the following three lines of code:

XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("myFile.xml", getClass()));
MyBusinessObject mbo = (MyBusinessObject) bf.getBean("exampleBusinessObject");

This code will work outside an application server: it doesn't even depend on J2EE, as the Spring IoC container is pure Java. The Spring Rich project (a framework for simplifying the development of Swing applications using Spring) demonstrates how Spring can be used outside a J2EE environment, as do Spring's integration testing features, discussed later in this article. Dependency Injection and the related functionality is too general and valuable to be confined to a J2EE, or server-side, environment.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux block IO(块输入输出)是Linux操作系统的IO子系统,用于管理块设备(例如硬盘和SSD)的访问。在多核系统上引入多队列SSD访问是一种优化措施。 传统上,Linux操作系统在处理块设备访问时,使用单个队列(queue)来处理所有IO请求。这种单队列设计对于单核系统来说是合适的,因为只有一个CPU核心可以处理IO请求。然而,在多核系统中,这种设计却成为了性能瓶颈,因为所有的IO请求都必须经过单个队列,即使有多个CPU核心是可用的。 为了解决这个问题,Linux引入了多队列SSD访问功能。这意味着在多核系统上,每个CPU核心都有一个独立的队列来处理IO请求。每个队列可以独立处理IO请求,而不会受到其他队列的干扰。这种设计可以提高系统的并发性和吞吐量。 多队列SSD访问还可以充分利用SSD设备的性能特点。SSD设备通常具有多个通道(channel)和多个闪存芯片(chip),每个通道和芯片都可以同时处理IO请求。通过将IO请求分配给多个队列,可以同时利用多个通道和芯片,从而提高SSD的性能。 在Linux中实现多队列SSD访问需要对内核进行相应的修改和配置。用户可以通过命令和配置文件来设置每个队列的属性和参数,以满足特定场景下的需求。 总之,通过引入多队列SSD访问,Linux在多核系统上可以更好地利用硬件资源,提高系统的性能和吞吐量。这是一个重要的优化措施,可以提高块设备访问的效率和响应速度。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值