[作者] Benoy Jose
[点评] 介绍的比较全面，但也稍嫌单薄，是Rod Johnson在邮件列表中给的连接，说明还不错。
Officially the spring season might be over but spring still seems to be lingering around. If you did not get the smell of the Spring Framework this summer then you might be missing something. You might be having your fingers crossed skeptically about another run of the mill Framework system that we have been seeing for the last couple of years. Hundreds of open source and propriety frameworks have sprung all over the place making it difficult to identify what each framework does, so the skepticism is understandable, but before we write off spring like one of those others it might be worth briefly peeking through it. Spring proposes a new paradigm, a pluggable, non- intrusive and robust framework.
Unlike other frameworks and APIs, spring does not impose itself wholly on to the design of a project. Spring is modular and has been divided logically into independent packages, which can function independently. The architects of an application have the flexibility to implement just a few spring packages and leave out most of the packages in spring. The "Spring Framework" would not feel bad with this attitude and on the contrary encourages users to introduce Spring into existing applications in a phased manner. So no matter what kind of framework you are using now Spring will co-exist with it without causing you nightmares and further more Spring will allow you to choose specific packages in Spring.
The "Struts" framework is no doubt a good framework to enhance the ability of the web tier, but the biggest drawback is the fact that it caters only to the web tier and leaves most of the Enterprise tier or middle tier to the fancy of the application architects. The Application architects need to provide an additional framework to deal with the enterprise tier and make sure that the new framework integrates well with the Struts framework. Spring tries to alleviate this problem by providing a comprehensive framework, which includes an MVC framework, an AOP integration framework, a JDBC integration framework, and an EJB integration framework. It also provides integration modules for major O/R mapping tools like Hibernate and JDO. Spring provides all these in a modular fashion without imposing any layer on to the user. Spring is not a take-it-or- leave-it kind of framework. It tries to seamlessly blend into the existing framework users have without hindrances. Spring also provides transaction management support using Java classes, email support packages using framework classes, web services support through proxies and many more features like the above. As mentioned earlier all these packages are optional and spring does not make any of them mandatory. Spring can seamlessly integrate with existing applications and provide specific functionality that you intend to provide with minimal demands for customization. A user can continue to use Struts for the web tier and toplink O/R for the database and meanwhile hook spring to provide e-mail services and web services support. Spring is based on the Inversion of Control/Dependency Injection pattern that has been making rounds in message boards all over the Internet.
Inversion of Control/Dependency Injection
Dependency injection is kind of an Inversion of Control pattern. It is proposed as an alternative for the Service Locator. Martin flower discusses the Dependency Injection pattern, Inversion of Control pattern and the differences it has with the service locator in an article at his site (See resources below).
Dependency Injection proposes separating the implementation of an object and the construction of objects that depend on them. The job of coordinating the implementation and construction is left to the Assembler code. The object that needs to be implemented does not need to instantiate the dependent objects and can rely on the assembler to do the job. The assembler will gather and instantiate, if necessary, all the dependent objects and make them available to the implemented object. Since the assembler does not depend on the code directly changes can be made to the assembler without any changes to the code. The assembler gathers the required classes through configuration files so a change in the assembler only needs changes to the configuration file. In this case the Assembler code would be the spring framework.
Since the classes are independent and are integrated through the assembler, independent testing of each class can be done without affecting other application codes.
There are three types of Dependency Injections.
Type 1 IOC also called Interface Injection In Type 1 IOC the injection is done though an interface. The interface will define the injection method and the implementation class has to implement this interface and provide concrete implementation for the injection method.
Type 2 IOC also called Setter Injection In Type 2 IOC the injection is done via a setter method. Type 2 IOC uses setter methods to get the dependent classes it needs.
Type 3 IOC also called Constructor Injection. In Type 3 IOC implementing class defines a constructor to get all its dependents. The dependent classes are defined in the constructor arguments.
How does Spring Work?
The idea is that beans follow the Dependency Injection pattern. Beans have information on the classes dependent on them. Beans define their own attributes within bean descriptors and also define the beans they depend on in the same descriptors. The Bean does not need to locate and instantiate these classes using service locators or JNDI calls. The Assembler (Spring Framework in this case) takes care of finding these classes based on the information provided in the descriptor and makes them available to the calling class. The service locator pattern does almost the same job, so why do we need another pattern to do it. The class that needs the dependent classes needs to tell the Service Locator as to which classes are required by it and moreover the responsibility of finding these classes and invoking them falls on the calling class. This makes the classes tightly coupled with each other making them difficult to unit test them separately. In the case of the Dependency Injection pattern the responsibility is shifted to the Assembler to load these classes. The assembler can make changes to the dependent classes by simply modifying the descriptor file. Martin Flower’s article in the resources shows the comparison between Dependency Injection and Service Locator with a good example.
Beans, BeanFactory and Application Context
The basic package in the spring framework is the org.springframework.beans package. Spring framework uses JavaBeans and this package provides most of the basic functionality to manipulate Java beans and provides the basic infrastructure for the other spring framework classes. This package also provides the basis for the "Dependency injection" pattern that Spring is based on.
There are two ways in which clients can use the functionality of the Spring Framework--the BeanFactory and the ApplicationContext. The BeanFactory is a generic factory, which stores the information about all the Spring Beans and allows the user to instantiate beans and manage them. The BeanFactory provides programmers with the facilities to implement the basic features of the Spring Framework. The ApplicationContext builds on top of the BeanFactory and inherits all the basic features of the Spring Framework. Apart from the basic features, ApplicationContext provides additional features like Event management, internationalization support and resource management.
The BeanFactory is particularly useful in low memory situations like in an Applet where having the whole API would be overkill. It provides the basic spring framework features and does not bring all the excess baggage that ApplicationContext has. ApplicationContext helps the user to use Spring in a framework oriented way while the BeanFactory offers a programmatic approach.
A BeanFactory is like a factory class that contains a collection of beans. The BeanFactory holds BeanDefinitions of multiple beans within itself and then instantiates the bean whenever asked for by clients.
XMLBeanFactory is a BeanFactory implementation provided within the Spring Framework. The XMLBeanFactory can read BeanDefinitions from a XML file directly. The XMLBeanFactory validates the XML using a DTD file called beans.dtd and checks for inconsistencies in the XML.
The Bean, which is stored in the BeanFactory, is the actual class that would carry the logic for the bean. Spring does not define any standards on how the bean needs to be structured. Any J2EE complaint bean structure is acceptable to Spring. Unlike Struts and other frameworks the beans do not need to implement any special Spring interfaces to make them work in the Spring Framework. Depending upon the kind of Inversion required the bean might have to follow the rules of the corresponding Inversion Dependency pattern. Spring supports only Constructor based injection and setter based injection. So a bean that used constructor-based injection should have to define constructors accordingly. Spring recommends setter-based injection over constructor-based injection as multiple constructors can make the bean huge and unmanageable.
A bean has one or more IDs associated with it. The ID should be unique within the BeanFactory it is contained in so that the BeanFactory can look up the bean using the ID. If a bean has multiple IDs they are defined as aliases for the bean.
The spring framework takes care of how the beans can be created and sent back to clients. Beans can be deployed as singletons or as non-singletons. If a bean is defined as a singleton then only one instance of the bean is created by the BeanFactory and returned when a request for the bean is made. All subsequent requests get the same instance which was created first. Non- singleton beans or Prototype beans can have multiple instances created. So each time a request is made for a bean to the BeanFactory a new instance is created and returned. The BeanFactory cannot do Lifecycle management of a prototype bean, as a new instance is created for each client who requests it(Lifecycle management of a bean is discussed in a subsequent section).
The BeanDefinition contains all the information required by the BeanFactory to instantiate the bean. This includes any dependencies the bean may have on other beans and how the bean needs to be configured in the container. When a request is made for a bean, the BeanFactory loads the corresponding BeanDefinition and instantiates the bean. The BeanDefinition has information on how the bean would be instantiated (Singleton or prototype) and callback methods defined in the Lifecycle section.
Lifecycle of a Bean
After the bean is initialized the BeanFactory can use callback methods to change the behavior of the bean in the BeanFactory. One of the callback methods is the init-method. The InitializingBean interface of the factory package gives the ability to do initialization work after properties are set up for a bean in the BeanFactory. The afterPropertiesSet () method in the InitializingBean interface allows the user to check if all the properties are set properly or if some operation has to be done after the properties have been set. All Bean classes that need to use this callback method need to implement the InitializingBean interface and implement the afterPropertiesSet () method. If the initialization work is minimal the user has the flexibility to designate a method within the bean, which will be invoked after bean properties have been set. The designated method, which would do the initialization, can be then be specified in the init-method attribute of the bean. Designating the init-method attribute can help to avoid implementing the InitializingBean interface.
In a similar fashion a DisposableBean interface is provided to do cleanup operations after a bean is destroyed. The bean has a destroy-method attribute that can used to designate a method within the bean, which will do the clean up work when the BeanFactory destroys the bean.
Spring can automatically resolve the dependent beans with the help of the BeanFactory. This facility can reduce the need to specify properties or constructor arguments.
Spring can check dependencies on a bean. It can check for values that should be set when a bean is initialized. If the check is not done the BeanFactory will set the default value. This behavior is not mandatory and can be turned on when required.
Spring can also manage multiple resource files for you.
Lets go through a sample to illustrate a few of the important ideas outlined above. Shown below is a snippet of XML that defines some BeanDefinitions of an actual bean.
<bean id = ‘helloWorldSample′ class=”com.helloworld.samples.HelloWorld” depends-on = ”boostrapClassId”><property name=”myString”>My famous Hello World Program</property><property name=”dependentClassId”> <ref bean = “firstDependentClass”/></property></bean> <bean id = “firstDependentClass” class = “com.helloworld.dependent.FirstDependentClass” destroy-method=”cleanupMethod”><property name=”dependentString”>My famous Hello World Program needs me</property><bean><bean id = “bootstrapClassId” class = “com.helloworld.config.BootStrapClass” init-method = “loadPropertiesFromFile”>
The sample above shows a BeanDefinition for a HelloWorld Class, which depends upon the BootStrapClass class. The property ‘myString’ would translate into a field into the bean with a default value specified within it. Since the BeanFactory knows that this bean requires the class FirstDependentClass, it would instantiate that class before it sets the reference for that class in the main bean. The property "dependentClassId" would also become a field in the bean with a reference to the FirstDependentClass. The loadProertiesFromFile method which is specified as a init method will be invoked after properties have been set to the BootStrapClass bean.
The ApplicationContext provides a framework type of API to Spring. The framework allows the user to use a ContextLoader to load beans. The ContextLoader does the job of the BeanFactory. The ApplicationContext provides all the features offered by BeanFactory and allows the user to add some additional features. Some of the features offered by the ApplicationContext are MessageSources, Resource management and Event Propagation.
MessageSource offers an i18n type messaging facility. Event Propagation allows Spring to have an event handling mechanism. The Event handling is done via the ApplicationEvent Class and the ApplicationListener interface. Every bean that’s wants to be notified of any events would implement the ApplicationListener Interface. Whenever an ApplicationEvent takes place the ApplicationContext knows about it. The ApplicationContext would notify the beans that are listening to this event.
Spring provides a lot of additional modules, which are optional to the framework. The User has the flexibility to use them, as they need them in their application. Some of these features are briefly discussed below.
Spring does not need Aspect Oriented Programming (AOP) to function as a framework. AOP enhances the Spring middleware support by providing declarative services. The declarative services can be used as replacements for the EJB declarative services used in CMTs. The EJB "declarative services" are provided by Spring using the "declarative transaction management service." Transaction management services are based on the spring transaction management, which will be discussed next.
Spring provides a simple programmatic transaction management API. Spring provides support for both declarative transaction management and programmatic transaction management. Declarative transaction management is used in EJB container managed transactions (CMT) and programmatic transaction management is used in EJB bean managed persistence. Additionally Spring can use transaction management to avoid the use of EJBs where not required.
Data Access using JDBC
The org.springframework.jdbc package provides all the JDBC related support required by the application. One of the interesting interfaces within this package is the SQLExceptionTranslator. The translator can help in providing translations from the very common and generic SQLException in to something more specific and informative. SQLExceptions are too generic and cannot provide any great information that can be passed back to the user. Spring’s wrapper (SQLExceptionTranslator) can implemented specific to a vendor and can provide informative error messages, which can be sent back to the user.
Spring provides integration with OR mapping tools like Hibernate, JDO and iBATIS. (For information on Hibernate see resources below). Spring provides very good support for Hibernate. Spring augments the power of Hibernate by providing support for Hibernate sessions, Hibernate transaction management and other features in Hibernate.
Spring Web Framework
Spring provides a pluggable MVC architecture. The users have a choice to use the web framework or continue to use their existing web framework. Spring separates the roles of the controller; the model object, the dispatcher and the handler object which makes it easier to customize them. Spring web framework is view agnostic and does not push the user to use only JSPs for the view. The user has the flexibility to use JSPs, XSLT, velocity templates etc to provide the view.
Spring And EJB
In a usual EJB application EJBs are located using service locators and managed using business delegates. These results in EJB JNDI look ups scattered all over the code. Spring can provide proxy objects, which can act as business delegates do the same job. These proxies can be configured into the BeanFactory and the ApplicationContext.
Spring is very new to the market and provides a new lease of life to framework development. Spring does not try to duplicate existing solutions; it tries to integrate with exiting solutions like JDO, AOP and Hibernate. However spring creates a new web framework to replace Struts, as the creators of Spring feel that Struts is not robust enough. Spring would generate great interest in the coming months as more people become aware of it and find it worthwhile to implement. Spring is portable across application servers, as it does not include any application server or platform specific requirements. This makes it compatible on major application servers like Websphere, Weblogic, Tomcat, Jboss and others like them. As of now spring can be integrated and used in a eclipse IDE, however as the versions improve spring should be providing support for more IDEs.
Benoy Jose is a web developer with over six years of experience in J2EE and Microsoft technologies. He is a Sun Certified programmer and enjoys writing technical and non-technical articles for various magazines.