Struts 1.1 final终于发布了。新特性包括对多个子应用程序的支持、DynaBean和BeanUtil、声明式异常处理、Validator等。熟悉Struts的人肯定早已在用Struts 1.1，下面这篇文章是给不熟悉的人看的。
Since the release of Struts 1.0, Struts has gradually become a de facto standard for MVC (a.k.a. Model-2) implementation for developing medium-to-large scale web-based applications on the Java platform. Struts fits nicely in the J2EE technology stack and fills the gap which is not addressed neither by the Servlet/JSP standard or the EJB standard. Giants like IBM and Oracle have started embracing and supporting Struts in their product set.
Built on the success of Struts 1.0, Struts 1.1 beta was released in March this year and showcased at JavaOne 2002 and the latest Struts 1.1 beta-2 was released just two weeks ago. The immediate question existing Struts users or evaluators will have is "Should I now consider to migrate my existing applications to Struts 1.1 and start to develop my new project on Struts 1.1?"
To answer this question, we need to consider what important new features are offered in 1.1 and examine what impacts there are on existing projects to migrate from 1.0 to 1.1.
Instead of overwhelming you with the full list of all the new features, I will go through the most significant enhancements in Struts 1.1 in this section.
One of the shortcomings in Struts 1.0 is manageability of the configuration file (commonly known as struts-config.xml) when the development of the application involves a sizable team. This problem arises because a Struts-based application must run under one controller servlet, the ActionServlet, and the ActionServlet can use only one struts-config.xml. It is not an issue when the project is relatively small and the team consists of a couple of developers; however, when the project size becomes significant and the project involves a large number of developers, maintaining all the mapping information in a single file becomes increasingly problematic.
Struts 1.1 solves this problem nicely by introducing multiple sub-applications. In Struts 1.1, each sub-application has its own struts-config.xml file. A large Struts-based application can thus be easily partitioned into largely independent modules, i.e. sub-applications, and each sub-team can maintain their struts-config.xml independently.
The sub-application scheme is a natural extension of the servlet context mapping scheme of the URI paths used by servlet containers. According to the Servlet standard, when the servlet container receives a request with a URL, the servlet container will try to match the prefix of the URI path to a deployed web-application in the container. What Struts 1.1 does is it maps the second prefix of the path to a sub-application. In effect, this prefix mapping scheme creates another level of namespace for each sub-application. For example, for the URI,
/myApp is the context path for a web-application called "myApp" and /module2 is the sub-app prefix for a Struts sub-application called "module2".
DynaBean and BeanUtils
Another major complaint usually heard amongst Struts 1.0 users is the extensive effort involved in writing the FormBean (a.k.a. ActionForm) classes.
Struts provides two-way automatic population between HTML forms and Java objects, the FormBeans. To take advantage of this however, you have to write one FormBean per HTML form. (In some use cases, a FormBean can actually be shared between multiple HTML forms. But those are specific cases.) Struts' FormBean standard follows faithfully the verbose JavaBean standard to define and access properties. Besides, to encourage a maintainable architecture, Struts enforces a pattern such that it is very difficult to 'reuse' a model-layer object (e.g. a ValueObject from the EJB tier) as a FormBean. Combining all these factors, a developer has to spend a significant amount of time to write tedious getters/setters for all the FormBean classes.
Struts 1.1 offers an alternative, Dynamic ActionForms, which are based on DynaBeans. Simply put, DynaBeans are type-safe name-value pairs (think HashMaps) but behave like normal JavaBeans with the help of the BeanUtils library. (Both the DynaBeans and the BeanUtils library were found to be useful and generic enough that they have been 'promoted' into Jakarta's Commons project.) With Dynamic ActionForms, instead of coding the tedious setters/getters, developers can declare the required properties in the struts-config.xml files. Struts will instantiate and initialize Dynamic ActionForm objects with the appropriate metadata. From then onwards, The Dynamic ActionForm instance is treated as if it is an ordinary JavaBean by Struts and the BeanUtils library.
Declarative Exception Handling
If you have developed web applications long enough, you will realize a recurring pattern emerges: when the backend (e.g. the EJB tier) throws you an exception, you nearly always need to display an error page corresponding to the type of that exception. Sooner or later, you will come up with a mechanism to use a lookup table (e.g. an HashMap) to lookup an error page from the exception class.
Struts 1.1 now provides a similar but more powerful mechanism to declare exception handling. In Struts 1.1, you can declare in the struts-config.xml the associations between an exception class and an exception handler. Using the default exception handler included in Struts, you can also specify the path of the error pages. With this information, Struts will automatically forward to the specified pages when an uncaught exception is thrown from an Action.
Like other facilities in Struts, the exception handlers are pluggable. You can write and define your own handler classes if needed.
The Validator is not exactly a new feature. The Validator has been in the contrib package in the distribution since Struts 1.0.1. Since then, part of it has now been refactored and moved into the Jakarta-Commons subproject and renamed the Commons-Validator and the Struts specific portion is now called the Struts-Validator. However, since it is in the contrib package, people may overlook it and it is worthwhile to mention it here.
Having gone through the list of exciting new features, it is time to examine the potentially negative impact of these enchacements. One of the highest priorities of the Struts' developer community when developing Struts 1.1 has been to achieve maximum backward compatibility with 1.0. Struts' developers have definitely done a good job in this regard; however, there are still some issues you need to be aware of.
To maintain backward compatibility, Struts 1.1 allows one default sub-application per application. The URI of the resources (i.e. JSPs, HTMLs, etc) in the default sub-application will have an empty sub-app prefix. This means when an existing 1.0 application is "dropped" into Struts 1.1, theoretically, it will automatically become the default sub-application.
Direct Requests to JSPs
To take the full advantage of sub-application support, Struts 1.1 stipulates the requirement that all requests must flow through the controller servlet, i.e. the ActionServlet. Effectively, this means all JSPs must be fronted by Actions. Instead of allowing direct requests to any of the JSPs, all requests must go through an Action and let the Action forward to the appropriate JSP.
This is perhaps the biggest impact of migration to Struts 1.1 if you have not followed this idiom in your applications. This restriction is required because without going through the ActionServlet, Struts navigation taglibs (e.g. <html:form> and <html:link>) used in the JSPs will not have the correct sub-app context to work with.
With the introduction of sub-applications, a more flexible way is introduced to configure each sub-application independently. Many of the configuration entries (e.g. resource bundle location, maximum upload file size, etc) that used to be defined in web.xml have now been moved to struts-config.xml. The original entries in web.xml are deprecated but will still be effective.
Both extension-mapped (e.g. *.do) and path-mapped (e.g. /do/*) Actions are supported in Struts 1.0. However, at the time of writing (August 2002), mulitple sub-applications will not work with path-mapped Actions. It is likely this issue will be resolved in the final release of Struts 1.1.
Action.execute() and Action.getResources()
In Struts 1.0, request handling logic is coded in Action.perform(); however, Action.perform() throws only IOException and SevletException. To facilitate the new declarative exception handling , the request handling method needs to throw Exception, the superclass of all the checked exceptions. Therefore, to both maintain backward compatibility and facilitate declarative exception handling, Action.perform() is now deprecated in favour of Action.execute().
You also have to be careful if you use DispatchAction in your existing applications. At the time of writing, the DispatchAction in Struts 1.1 beta has not yet been updated to use execute(). (A bug report has been filed in Struts' bug database.) Therefore, without modifying the DispatchAction class yourself, declarative exception handling will not work with DispatchAction subclasses.
In addition, Action.getResources() is now deprecated. Instead, you should call Action.getResources(HttpServletRequest) instead. This allows Struts to return to you the sub-application specific message resources. Otherwise, the message resources for the default sub-app will be used.
Struts 1.1 now depends on a handful of libraries from other Jakarta subprojects (e.g. Commons-Logging, Commons-Collections, etc.). Some of these libraries may cause classloading conflicts in some servlet containers. So far, people have reported in the mailing list the classloading problem of commons-digester/jaxp1.1, and commons-logging causing deployment difficulties in Struts 1.1 applications running on Weblogic 6.0. (The problems have been corrected in Weblogic 6.1 and 7.0.)
Resources under WEB-INF
According to the Servlet specification, resources (e.g. JSP files) stored under WEB-INF are protected and cannot be accessed directly by the browsers. One design idiom for Struts 1.0 is to put all the JSP files under WEB-INF and front them by Actions so that clients cannot illegally access the JSPs.
With the introduction of sub-application prefixes in Struts 1.1, mapping resources under WEB-INF gets complicated. Extra configuration steps utilizing the pagePattern and forwardPattern attributes of the <controller> element in struts-config.xml is required to inform Struts to construct the paths correctly. More specifically, you need to set these attributes to the pattern "/WEB-INF/$A$P".
Miscellenous issues for advanced users
The following issues are relevant to you if you are a power Struts user and make use of some advanced features in Struts:
- If your application depends on the ActionFormBeans, ActionForwards or ActionMappings servlet context attributes, you have to get the information they need from the ApplicationConfig object instead.
- If you subclass ActionServlet, you may have to retrofit it back to that of Struts 1.1 or the new RequestProcessor class as ActionServlet has been refactored.
So, "Should I consider upgrading to Struts 1.1?" Here are my recommendations.
For Existing Small Projects
If you have a relatively small project and are happy with what Struts 1.0 currently offers, it makes sense to stay with Struts 1.0.
For Existing Medium-to-Large Projects
If you have a large project, the multiple sub-application feature is definitely the overriding factor in influencing your decision. The litmus test is to ask yourself this question: "Is the existing struts-config.xml file getting very difficult to manage due to its sheer file size?" If the answer is yes, you should consider doing a pilot trial to replace Struts 1.0 with Struts 1.1 in your development environment without partitioning your application into sub-applications first.
Since Struts 1.1 can treat your existing application simply as the default sub-application, the replacement should be straightforward. However, to test if your application is actually Struts 1.1 compliant, you should configure it as a sub-application with a non-empty prefix and go through some representative use cases to test and see if the application works as it used to be. Before you embark onto the pilot trial, you have to make sure all of your developers are aware of the issues listed in the previous section, particularly the requirement that all requests must go through the controller servlet as it is quite likely that that may not the case in your application. You need their input to decide if the migration is feasible or not.
Once the pilot trial is successful and you decide to migrate to Struts 1.1, you can start to work out how to partition the application into sub-applications with adequate granularity. Being able to partition the application into sub-applications, you may also want to consider reorganizing your team structure and the project structure of your source code control to align with the partitioning.
For New Projects
If your project is a new one, the decision to use Struts 1.1 mainly depends on the amount of risk your project can take. Struts 1.0 is a proven technology but Struts 1.1 is still in beta. Besides, migrating backwards is always more difficult than migrating forwards. Once you start with Struts 1.1, it will involve a lot of hard work to go back to Struts 1.0. While the extra risk in using Struts 1.1 is generally very low (as the quality of work from Apache's developer community has been consistently very good), it is still a non-negligible factor you have to consider.
About the Author
John Yu is the co-founder and chief scientist at Scioworks Technologies, an enterprise software consulting and products company based in Singapore. He is the primary designer of Scioworks Camino, a visual modelling tool for developing Struts applications. John's J2EE/EJB experience dates back to a pilot project he led implemented on Tengah, what Weblogic AppServer was called before Weblogic was acquired by BEA, when he was a designer/architect working in Australia. John holds a Masters degree of Computing from Monash University, Australia and a Postgraduate Diploma in Management from Melbourne Business School, Australia. He was awarded the Telstra Research Fellowship and the Australia Postgraduate Award.